mirror of
https://github.com/facebook/rocksdb.git
synced 2026-11-06 11:02:36 -05:00
Compare commits
17
Commits
6.10.fb
...
ajkr-patch-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
641948c72a | ||
|
|
e9ba4ba348 | ||
|
|
ac3ae1df0b | ||
|
|
1f20df2f38 | ||
|
|
c1e1185b7a | ||
|
|
06c3b85b9a | ||
|
|
53f84470d2 | ||
|
|
c21c459771 | ||
|
|
394f2bbd13 | ||
|
|
a96461d169 | ||
|
|
2f1700c8c5 | ||
|
|
91bc0130fa | ||
|
|
5584595f80 | ||
|
|
5a61e7864d | ||
|
|
a00ddf1574 | ||
|
|
680c416348 | ||
|
|
d9e170d82b |
No files matched your search
@@ -1,6 +1,15 @@
|
||||
# Rocksdb Change Log
|
||||
## Unreleased
|
||||
### Bug Fixes
|
||||
* Fix consistency checking error swallowing in some cases when options.force_consistency_checks = true.
|
||||
|
||||
### Public API Change
|
||||
* Flush(..., column_family) may return Status::ColumnFamilyDropped() instead of Status::InvalidArgument() if column_family is dropped while processing the flush request.
|
||||
* BlobDB now explicitly disallows using the default column family's storage directories as blob directory.
|
||||
* DeleteRange now returns `Status::InvalidArgument` if the range's end key comes before its start key according to the user comparator. Previously the behavior was undefined.
|
||||
|
||||
## 6.10 (5/2/2020)
|
||||
### Bug Fixes
|
||||
* Fix wrong result being read from ingested file. May happen when a key in the file happen to be prefix of another key also in the file. The issue can further cause more data corruption. The issue exists with rocksdb >= 5.0.0 since DB::IngestExternalFile() was introduced.
|
||||
* Finish implementation of BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey. It's now ready for use. Significantly reduces read amplification in some setups, especially for iterator seeks.
|
||||
* Fix a bug by updating CURRENT file so that it points to the correct MANIFEST file after best-efforts recovery.
|
||||
|
||||
@@ -106,3 +106,6 @@ LzLabs is using RocksDB as a storage engine in their multi-database distributed
|
||||
## Nebula Graph
|
||||
|
||||
[Nebula Graph](https://github.com/vesoft-inc/nebula) is a distributed, scalable, lightning-fast, open source graph database capable of hosting super large scale graphs with dozens of billions of vertices (nodes) and trillions of edges, with milliseconds of latency.
|
||||
|
||||
## YugabyteDB
|
||||
[YugabyteDB](https://www.yugabyte.com/) is an open source, high performance, distributed SQL database that uses RocksDB as its storage layer. For more information, please see https://github.com/yugabyte/yugabyte-db/.
|
||||
@@ -93,9 +93,13 @@ class LevelCompactionBuilder {
|
||||
// otherwise, returns false.
|
||||
bool PickIntraL0Compaction();
|
||||
|
||||
void PickExpiredTtlFiles();
|
||||
|
||||
void PickFilesMarkedForPeriodicCompaction();
|
||||
// Picks a file from level_files to compact.
|
||||
// level_files is a vector of (level, file metadata) in ascending order of
|
||||
// level. If compact_to_next_level is true, compact the file to the next
|
||||
// level, otherwise, compact to the same level as the input file.
|
||||
void PickFileToCompact(
|
||||
const autovector<std::pair<int, FileMetaData*>>& level_files,
|
||||
bool compact_to_next_level);
|
||||
|
||||
const std::string& cf_name_;
|
||||
VersionStorageInfo* vstorage_;
|
||||
@@ -124,72 +128,34 @@ class LevelCompactionBuilder {
|
||||
static const int kMinFilesForIntraL0Compaction = 4;
|
||||
};
|
||||
|
||||
void LevelCompactionBuilder::PickExpiredTtlFiles() {
|
||||
if (vstorage_->ExpiredTtlFiles().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
void LevelCompactionBuilder::PickFileToCompact(
|
||||
const autovector<std::pair<int, FileMetaData*>>& level_files,
|
||||
bool compact_to_next_level) {
|
||||
for (auto& level_file : level_files) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
start_level_ = level_file.first;
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
|
||||
if ((start_level_ == vstorage_->num_non_empty_levels() - 1) ||
|
||||
if ((compact_to_next_level &&
|
||||
start_level_ == vstorage_->num_non_empty_levels() - 1) ||
|
||||
(start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty())) {
|
||||
return false;
|
||||
continue;
|
||||
}
|
||||
if (compact_to_next_level) {
|
||||
output_level_ =
|
||||
(start_level_ == 0) ? vstorage_->base_level() : start_level_ + 1;
|
||||
} else {
|
||||
output_level_ = start_level_;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->ExpiredTtlFiles()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
void LevelCompactionBuilder::PickFilesMarkedForPeriodicCompaction() {
|
||||
if (vstorage_->FilesMarkedForPeriodicCompaction().empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto continuation = [&](std::pair<int, FileMetaData*> level_file) {
|
||||
// If it's being compacted it has nothing to do here.
|
||||
// If this assert() fails that means that some function marked some
|
||||
// files as being_compacted, but didn't call ComputeCompactionScore()
|
||||
assert(!level_file.second->being_compacted);
|
||||
output_level_ = start_level_ = level_file.first;
|
||||
|
||||
if (start_level_ == 0 &&
|
||||
!compaction_picker_->level0_compactions_in_progress()->empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
start_level_inputs_.files = {level_file.second};
|
||||
start_level_inputs_.level = start_level_;
|
||||
return compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_);
|
||||
};
|
||||
|
||||
for (auto& level_file : vstorage_->FilesMarkedForPeriodicCompaction()) {
|
||||
if (continuation(level_file)) {
|
||||
// found the compaction!
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
start_level_inputs_.files.clear();
|
||||
}
|
||||
|
||||
@@ -238,63 +204,46 @@ void LevelCompactionBuilder::SetupInitialFiles() {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Compaction scores are sorted in descending order, no further scores
|
||||
// will be >= 1.
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!start_level_inputs_.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// if we didn't find a compaction, check if there are any files marked for
|
||||
// compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
parent_index_ = base_index_ = -1;
|
||||
parent_index_ = base_index_ = -1;
|
||||
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_,
|
||||
&start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
compaction_picker_->PickFilesMarkedForCompaction(
|
||||
cf_name_, vstorage_, &start_level_, &output_level_, &start_level_inputs_);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kFilesMarkedForCompaction;
|
||||
return;
|
||||
}
|
||||
|
||||
// Bottommost Files Compaction on deleting tombstones
|
||||
if (start_level_inputs_.empty()) {
|
||||
size_t i;
|
||||
for (i = 0; i < vstorage_->BottommostFilesMarkedForCompaction().size();
|
||||
++i) {
|
||||
auto& level_and_file = vstorage_->BottommostFilesMarkedForCompaction()[i];
|
||||
assert(!level_and_file.second->being_compacted);
|
||||
start_level_inputs_.level = output_level_ = start_level_ =
|
||||
level_and_file.first;
|
||||
start_level_inputs_.files = {level_and_file.second};
|
||||
if (compaction_picker_->ExpandInputsToCleanCut(cf_name_, vstorage_,
|
||||
&start_level_inputs_)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == vstorage_->BottommostFilesMarkedForCompaction().size()) {
|
||||
start_level_inputs_.clear();
|
||||
} else {
|
||||
assert(!start_level_inputs_.empty());
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
return;
|
||||
}
|
||||
PickFileToCompact(vstorage_->BottommostFilesMarkedForCompaction(), false);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kBottommostFiles;
|
||||
return;
|
||||
}
|
||||
|
||||
// TTL Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickExpiredTtlFiles();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kTtl;
|
||||
return;
|
||||
}
|
||||
PickFileToCompact(vstorage_->ExpiredTtlFiles(), true);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kTtl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Periodic Compaction
|
||||
if (start_level_inputs_.empty()) {
|
||||
PickFilesMarkedForPeriodicCompaction();
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kPeriodicCompaction;
|
||||
return;
|
||||
}
|
||||
PickFileToCompact(vstorage_->FilesMarkedForPeriodicCompaction(), false);
|
||||
if (!start_level_inputs_.empty()) {
|
||||
compaction_reason_ = CompactionReason::kPeriodicCompaction;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-2
@@ -1774,6 +1774,28 @@ TEST_F(DBBasicTest, IncrementalRecoveryNoCorrupt) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DBBasicTest, BestEffortsRecoveryWithVersionBuildingFailure) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
options.best_efforts_recovery = true;
|
||||
Status s = TryReopen(options);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
namespace {
|
||||
class TableFileListener : public EventListener {
|
||||
@@ -2511,9 +2533,8 @@ TEST_F(DBBasicTestMultiGetDeadline, MultiGetDeadlineExceeded) {
|
||||
std::shared_ptr<DBBasicTestMultiGetDeadline::DeadlineFS> fs(
|
||||
new DBBasicTestMultiGetDeadline::DeadlineFS(env_));
|
||||
std::unique_ptr<Env> env(new CompositeEnvWrapper(env_, fs));
|
||||
env_->no_slowdown_ = true;
|
||||
env_->time_elapse_only_sleep_.store(true);
|
||||
Options options = CurrentOptions();
|
||||
env_->SetTimeElapseOnlySleep(&options);
|
||||
|
||||
std::shared_ptr<Cache> cache = NewLRUCache(1048576);
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
@@ -5024,10 +5024,11 @@ TEST_P(DBCompactionTestWithParam, FixFileIngestionCompactionDeadlock) {
|
||||
|
||||
TEST_F(DBCompactionTest, ConsistencyFailTest) {
|
||||
Options options = CurrentOptions();
|
||||
options.force_consistency_checks = true;
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency", [&](void* arg) {
|
||||
"VersionBuilder::CheckConsistency0", [&](void* arg) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
@@ -5046,6 +5047,48 @@ TEST_F(DBCompactionTest, ConsistencyFailTest) {
|
||||
|
||||
ASSERT_NOK(Put("foo", "bar"));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(DBCompactionTest, ConsistencyFailTest2) {
|
||||
Options options = CurrentOptions();
|
||||
options.force_consistency_checks = true;
|
||||
options.target_file_size_base = 1000;
|
||||
options.level0_file_num_compaction_trigger = 2;
|
||||
BlockBasedTableOptions bbto;
|
||||
bbto.block_size = 400; // small block size
|
||||
options.table_factory.reset(new BlockBasedTableFactory(bbto));
|
||||
DestroyAndReopen(options);
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistency1", [&](void* arg) {
|
||||
auto p =
|
||||
reinterpret_cast<std::pair<FileMetaData**, FileMetaData**>*>(arg);
|
||||
// just swap the two FileMetaData so that we hit error
|
||||
// in CheckConsistency funcion
|
||||
FileMetaData* temp = *(p->first);
|
||||
*(p->first) = *(p->second);
|
||||
*(p->second) = temp;
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Random rnd(301);
|
||||
std::string value = RandomString(&rnd, 1000);
|
||||
|
||||
ASSERT_OK(Put("foo1", value));
|
||||
ASSERT_OK(Put("z", ""));
|
||||
Flush();
|
||||
ASSERT_OK(Put("foo2", value));
|
||||
ASSERT_OK(Put("z", ""));
|
||||
Flush();
|
||||
|
||||
// This probably returns non-OK, but we rely on the next Put()
|
||||
// to determine the DB is frozen.
|
||||
dbfull()->TEST_WaitForCompact();
|
||||
ASSERT_NOK(Put("foo", "bar"));
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
void IngestOneKeyValue(DBImpl* db, const std::string& key,
|
||||
|
||||
+21
-10
@@ -90,6 +90,9 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
mutex_.Unlock();
|
||||
status = AtomicFlushMemTables(cfds, FlushOptions(),
|
||||
FlushReason::kGetLiveFiles);
|
||||
if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
mutex_.Lock();
|
||||
} else {
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
@@ -103,8 +106,10 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
TEST_SYNC_POINT("DBImpl::GetLiveFiles:2");
|
||||
mutex_.Lock();
|
||||
cfd->UnrefAndTryDelete();
|
||||
if (!status.ok()) {
|
||||
if (!status.ok() && !status.IsColumnFamilyDropped()) {
|
||||
break;
|
||||
} else if (status.IsColumnFamilyDropped()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,27 +123,33 @@ Status DBImpl::GetLiveFiles(std::vector<std::string>& ret,
|
||||
}
|
||||
}
|
||||
|
||||
// Make a set of all of the live *.sst files
|
||||
std::vector<FileDescriptor> live;
|
||||
// Make a set of all of the live table and blob files
|
||||
std::vector<uint64_t> live_table_files;
|
||||
std::vector<uint64_t> live_blob_files;
|
||||
for (auto cfd : *versions_->GetColumnFamilySet()) {
|
||||
if (cfd->IsDropped()) {
|
||||
continue;
|
||||
}
|
||||
cfd->current()->AddLiveFiles(&live);
|
||||
cfd->current()->AddLiveFiles(&live_table_files, &live_blob_files);
|
||||
}
|
||||
|
||||
ret.clear();
|
||||
ret.reserve(live.size() + 3); // *.sst + CURRENT + MANIFEST + OPTIONS
|
||||
ret.reserve(live_table_files.size() + live_blob_files.size() +
|
||||
3); // for CURRENT + MANIFEST + OPTIONS
|
||||
|
||||
// create names of the live files. The names are not absolute
|
||||
// paths, instead they are relative to dbname_;
|
||||
for (const auto& live_file : live) {
|
||||
ret.push_back(MakeTableFileName("", live_file.GetNumber()));
|
||||
for (const auto& table_file_number : live_table_files) {
|
||||
ret.emplace_back(MakeTableFileName("", table_file_number));
|
||||
}
|
||||
|
||||
ret.push_back(CurrentFileName(""));
|
||||
ret.push_back(DescriptorFileName("", versions_->manifest_file_number()));
|
||||
ret.push_back(OptionsFileName("", versions_->options_file_number()));
|
||||
for (const auto& blob_file_number : live_blob_files) {
|
||||
ret.emplace_back(BlobFileName("", blob_file_number));
|
||||
}
|
||||
|
||||
ret.emplace_back(CurrentFileName(""));
|
||||
ret.emplace_back(DescriptorFileName("", versions_->manifest_file_number()));
|
||||
ret.emplace_back(OptionsFileName("", versions_->options_file_number()));
|
||||
|
||||
// find length of manifest file while holding the mutex lock
|
||||
*manifest_file_size = versions_->manifest_file_size();
|
||||
|
||||
@@ -969,6 +969,12 @@ class DBImpl : public DB {
|
||||
void TEST_WaitForPersistStatsRun(std::function<void()> callback) const;
|
||||
bool TEST_IsPersistentStatsEnabled() const;
|
||||
size_t TEST_EstimateInMemoryStatsHistorySize() const;
|
||||
|
||||
VersionSet* TEST_GetVersionSet() const { return versions_.get(); }
|
||||
|
||||
const std::unordered_set<uint64_t>& TEST_GetFilesGrabbedForPurge() const {
|
||||
return files_grabbed_for_purge_;
|
||||
}
|
||||
#endif // NDEBUG
|
||||
|
||||
protected:
|
||||
@@ -1935,7 +1941,7 @@ class DBImpl : public DB {
|
||||
std::unordered_map<uint64_t, PurgeFileInfo> purge_files_;
|
||||
|
||||
// A vector to store the file numbers that have been assigned to certain
|
||||
// JobContext. Current implementation tracks ssts only.
|
||||
// JobContext. Current implementation tracks table and blob files only.
|
||||
std::unordered_set<uint64_t> files_grabbed_for_purge_;
|
||||
|
||||
// A queue to store log writers to close
|
||||
|
||||
@@ -1906,7 +1906,7 @@ Status DBImpl::WaitForFlushMemTables(
|
||||
}
|
||||
}
|
||||
if (1 == num_dropped && 1 == num) {
|
||||
return Status::InvalidArgument("Cannot flush a dropped CF");
|
||||
return Status::ColumnFamilyDropped();
|
||||
}
|
||||
// Column families involved in this flush request have either been dropped
|
||||
// or finished flush. Then it's time to finish waiting.
|
||||
|
||||
+40
-22
@@ -36,7 +36,7 @@ uint64_t DBImpl::MinObsoleteSstNumberToKeep() {
|
||||
return std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
|
||||
// * Returns the list of live files in 'sst_live'
|
||||
// * Returns the list of live files in 'sst_live' and 'blob_live'.
|
||||
// If it's doing full scan:
|
||||
// * Returns the list of all files in the filesystem in
|
||||
// 'full_scan_candidate_files'.
|
||||
@@ -77,12 +77,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
// Since job_context->min_pending_output is set, until file scan finishes,
|
||||
// mutex_ cannot be released. Otherwise, we might see no min_pending_output
|
||||
// here but later find newer generated unfinalized files while scanning.
|
||||
if (!pending_outputs_.empty()) {
|
||||
job_context->min_pending_output = *pending_outputs_.begin();
|
||||
} else {
|
||||
// delete all of them
|
||||
job_context->min_pending_output = std::numeric_limits<uint64_t>::max();
|
||||
}
|
||||
job_context->min_pending_output = MinObsoleteSstNumberToKeep();
|
||||
|
||||
// Get obsolete files. This function will also update the list of
|
||||
// pending files in VersionSet().
|
||||
@@ -90,13 +85,18 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
&job_context->sst_delete_files, &job_context->blob_delete_files,
|
||||
&job_context->manifest_delete_files, job_context->min_pending_output);
|
||||
|
||||
// Mark the elements in job_context->sst_delete_files as grabbedForPurge
|
||||
// so that other threads calling FindObsoleteFiles with full_scan=true
|
||||
// will not add these files to candidate list for purge.
|
||||
// Mark the elements in job_context->sst_delete_files and
|
||||
// job_context->blob_delete_files as "grabbed for purge" so that other threads
|
||||
// calling FindObsoleteFiles with full_scan=true will not add these files to
|
||||
// candidate list for purge.
|
||||
for (const auto& sst_to_del : job_context->sst_delete_files) {
|
||||
MarkAsGrabbedForPurge(sst_to_del.metadata->fd.GetNumber());
|
||||
}
|
||||
|
||||
for (const auto& blob_file : job_context->blob_delete_files) {
|
||||
MarkAsGrabbedForPurge(blob_file.GetBlobFileNumber());
|
||||
}
|
||||
|
||||
// store the current filenum, lognum, etc
|
||||
job_context->manifest_file_number = versions_->manifest_file_number();
|
||||
job_context->pending_manifest_file_number =
|
||||
@@ -104,7 +104,7 @@ void DBImpl::FindObsoleteFiles(JobContext* job_context, bool force,
|
||||
job_context->log_number = MinLogNumberToKeep();
|
||||
job_context->prev_log_number = versions_->prev_log_number();
|
||||
|
||||
versions_->AddLiveFiles(&job_context->sst_live);
|
||||
versions_->AddLiveFiles(&job_context->sst_live, &job_context->blob_live);
|
||||
if (doing_the_full_scan) {
|
||||
InfoLogPrefix info_log_prefix(!immutable_db_options_.db_log_dir.empty(),
|
||||
dbname_);
|
||||
@@ -257,8 +257,11 @@ bool CompareCandidateFile(const JobContext::CandidateFileInfo& first,
|
||||
void DBImpl::DeleteObsoleteFileImpl(int job_id, const std::string& fname,
|
||||
const std::string& path_to_sync,
|
||||
FileType type, uint64_t number) {
|
||||
TEST_SYNC_POINT_CALLBACK("DBImpl::DeleteObsoleteFileImpl::BeforeDeletion",
|
||||
const_cast<std::string*>(&fname));
|
||||
|
||||
Status file_deletion_status;
|
||||
if (type == kTableFile || type == kLogFile) {
|
||||
if (type == kTableFile || type == kBlobFile || type == kLogFile) {
|
||||
file_deletion_status =
|
||||
DeleteDBFile(&immutable_db_options_, fname, path_to_sync,
|
||||
/*force_bg=*/false, /*force_fg=*/!wal_in_db_path_);
|
||||
@@ -304,19 +307,19 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
// FindObsoleteFiles() should've populated this so nonzero
|
||||
assert(state.manifest_file_number != 0);
|
||||
|
||||
// Now, convert live list to an unordered map, WITHOUT mutex held;
|
||||
// set is slow.
|
||||
std::unordered_map<uint64_t, const FileDescriptor*> sst_live_map;
|
||||
for (const FileDescriptor& fd : state.sst_live) {
|
||||
sst_live_map[fd.GetNumber()] = &fd;
|
||||
}
|
||||
// Now, convert lists to unordered sets, WITHOUT mutex held; set is slow.
|
||||
std::unordered_set<uint64_t> sst_live_set(state.sst_live.begin(),
|
||||
state.sst_live.end());
|
||||
std::unordered_set<uint64_t> blob_live_set(state.blob_live.begin(),
|
||||
state.blob_live.end());
|
||||
std::unordered_set<uint64_t> log_recycle_files_set(
|
||||
state.log_recycle_files.begin(), state.log_recycle_files.end());
|
||||
|
||||
auto candidate_files = state.full_scan_candidate_files;
|
||||
candidate_files.reserve(
|
||||
candidate_files.size() + state.sst_delete_files.size() +
|
||||
state.log_delete_files.size() + state.manifest_delete_files.size());
|
||||
state.blob_delete_files.size() + state.log_delete_files.size() +
|
||||
state.manifest_delete_files.size());
|
||||
// We may ignore the dbname when generating the file names.
|
||||
for (auto& file : state.sst_delete_files) {
|
||||
candidate_files.emplace_back(
|
||||
@@ -327,6 +330,11 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
file.DeleteMetadata();
|
||||
}
|
||||
|
||||
for (const auto& blob_file : state.blob_delete_files) {
|
||||
candidate_files.emplace_back(BlobFileName(blob_file.GetBlobFileNumber()),
|
||||
blob_file.GetPath());
|
||||
}
|
||||
|
||||
for (auto file_num : state.log_delete_files) {
|
||||
if (file_num > 0) {
|
||||
candidate_files.emplace_back(LogFileName(file_num),
|
||||
@@ -413,12 +421,19 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
case kTableFile:
|
||||
// If the second condition is not there, this makes
|
||||
// DontDeletePendingOutputs fail
|
||||
keep = (sst_live_map.find(number) != sst_live_map.end()) ||
|
||||
keep = (sst_live_set.find(number) != sst_live_set.end()) ||
|
||||
number >= state.min_pending_output;
|
||||
if (!keep) {
|
||||
files_to_del.insert(number);
|
||||
}
|
||||
break;
|
||||
case kBlobFile:
|
||||
keep = number >= state.min_pending_output ||
|
||||
(blob_live_set.find(number) != blob_live_set.end());
|
||||
if (!keep) {
|
||||
files_to_del.insert(number);
|
||||
}
|
||||
break;
|
||||
case kTempFile:
|
||||
// Any temp files that are currently being written to must
|
||||
// be recorded in pending_outputs_, which is inserted into "live".
|
||||
@@ -428,7 +443,8 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
//
|
||||
// TODO(yhchiang): carefully modify the third condition to safely
|
||||
// remove the temp options files.
|
||||
keep = (sst_live_map.find(number) != sst_live_map.end()) ||
|
||||
keep = (sst_live_set.find(number) != sst_live_set.end()) ||
|
||||
(blob_live_set.find(number) != blob_live_set.end()) ||
|
||||
(number == state.pending_manifest_file_number) ||
|
||||
(to_delete.find(kOptionsFileNamePrefix) != std::string::npos);
|
||||
break;
|
||||
@@ -451,7 +467,6 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
case kDBLockFile:
|
||||
case kIdentityFile:
|
||||
case kMetaDatabase:
|
||||
case kBlobFile:
|
||||
keep = true;
|
||||
break;
|
||||
}
|
||||
@@ -467,6 +482,9 @@ void DBImpl::PurgeObsoleteFiles(JobContext& state, bool schedule_only) {
|
||||
TableCache::Evict(table_cache_.get(), number);
|
||||
fname = MakeTableFileName(candidate_file.file_path, number);
|
||||
dir_to_sync = candidate_file.file_path;
|
||||
} else if (type == kBlobFile) {
|
||||
fname = BlobFileName(candidate_file.file_path, number);
|
||||
dir_to_sync = candidate_file.file_path;
|
||||
} else {
|
||||
dir_to_sync =
|
||||
(type == kLogFile) ? immutable_db_options_.wal_dir : dbname_;
|
||||
|
||||
@@ -45,6 +45,8 @@ class DBSecondaryTest : public DBTestBase {
|
||||
|
||||
void OpenSecondary(const Options& options);
|
||||
|
||||
Status TryOpenSecondary(const Options& options);
|
||||
|
||||
void OpenSecondaryWithColumnFamilies(
|
||||
const std::vector<std::string>& column_families, const Options& options);
|
||||
|
||||
@@ -70,9 +72,13 @@ class DBSecondaryTest : public DBTestBase {
|
||||
};
|
||||
|
||||
void DBSecondaryTest::OpenSecondary(const Options& options) {
|
||||
ASSERT_OK(TryOpenSecondary(options));
|
||||
}
|
||||
|
||||
Status DBSecondaryTest::TryOpenSecondary(const Options& options) {
|
||||
Status s =
|
||||
DB::OpenAsSecondary(options, dbname_, secondary_path_, &db_secondary_);
|
||||
ASSERT_OK(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void DBSecondaryTest::OpenSecondaryWithColumnFamilies(
|
||||
@@ -858,6 +864,56 @@ TEST_F(DBSecondaryTest, CheckConsistencyWhenOpen) {
|
||||
thread.join();
|
||||
ASSERT_TRUE(called);
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, StartFromInconsistent) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Options options1;
|
||||
Status s = TryOpenSecondary(options1);
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
|
||||
TEST_F(DBSecondaryTest, InconsistencyDuringCatchUp) {
|
||||
Options options = CurrentOptions();
|
||||
DestroyAndReopen(options);
|
||||
ASSERT_OK(Put("foo", "value"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
Options options1;
|
||||
OpenSecondary(options1);
|
||||
|
||||
{
|
||||
std::string value;
|
||||
ASSERT_OK(db_secondary_->Get(ReadOptions(), "foo", &value));
|
||||
ASSERT_EQ("value", value);
|
||||
}
|
||||
|
||||
ASSERT_OK(Put("bar", "value1"));
|
||||
ASSERT_OK(Flush());
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
ASSERT_NE(nullptr, arg);
|
||||
*(reinterpret_cast<Status*>(arg)) =
|
||||
Status::Corruption("Inject corruption");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
Status s = db_secondary_->TryCatchUpWithPrimary();
|
||||
ASSERT_TRUE(s.IsCorruption());
|
||||
}
|
||||
#endif //! ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
@@ -47,6 +47,21 @@ TEST_F(DBRangeDelTest, WriteBatchWithIndexNotSupported) {
|
||||
ASSERT_TRUE(indexedBatch.DeleteRange("dr1", "dr1").IsNotSupported());
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, EndSameAsStartCoversNothing) {
|
||||
ASSERT_OK(db_->Put(WriteOptions(), "b", "val"));
|
||||
ASSERT_OK(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "b", "b"));
|
||||
ASSERT_EQ("val", Get("b"));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, EndComesBeforeStartInvalidArgument) {
|
||||
db_->Put(WriteOptions(), "b", "val");
|
||||
ASSERT_TRUE(
|
||||
db_->DeleteRange(WriteOptions(), db_->DefaultColumnFamily(), "b", "a")
|
||||
.IsInvalidArgument());
|
||||
ASSERT_EQ("val", Get("b"));
|
||||
}
|
||||
|
||||
TEST_F(DBRangeDelTest, FlushOutputHasOnlyRangeTombstones) {
|
||||
do {
|
||||
DestroyAndReopen(CurrentOptions());
|
||||
|
||||
+7
-19
@@ -356,37 +356,25 @@ TEST_F(DBSSTTest, RateLimitedDelete) {
|
||||
"InstrumentedCondVar::TimedWaitInternal", [&](void* arg) {
|
||||
// Turn timed wait into a simulated sleep
|
||||
uint64_t* abs_time_us = static_cast<uint64_t*>(arg);
|
||||
int64_t cur_time = 0;
|
||||
env_->GetCurrentTime(&cur_time);
|
||||
if (*abs_time_us > static_cast<uint64_t>(cur_time)) {
|
||||
env_->addon_time_.fetch_add(*abs_time_us -
|
||||
static_cast<uint64_t>(cur_time));
|
||||
uint64_t cur_time = env_->NowMicros();
|
||||
if (*abs_time_us > cur_time) {
|
||||
env_->addon_time_.fetch_add(*abs_time_us - cur_time);
|
||||
}
|
||||
|
||||
// Randomly sleep shortly
|
||||
env_->addon_time_.fetch_add(
|
||||
static_cast<uint64_t>(Random::GetTLSInstance()->Uniform(10)));
|
||||
|
||||
// Set wait until time to before current to force not to sleep.
|
||||
int64_t real_cur_time = 0;
|
||||
Env::Default()->GetCurrentTime(&real_cur_time);
|
||||
*abs_time_us = static_cast<uint64_t>(real_cur_time);
|
||||
// Set wait until time to before (actual) current time to force not
|
||||
// to sleep
|
||||
*abs_time_us = Env::Default()->NowMicros();
|
||||
});
|
||||
|
||||
ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
env_->no_slowdown_ = true;
|
||||
env_->time_elapse_only_sleep_ = true;
|
||||
Options options = CurrentOptions();
|
||||
env_->SetTimeElapseOnlySleep(&options);
|
||||
options.disable_auto_compactions = true;
|
||||
// Need to disable stats dumping and persisting which also use
|
||||
// RepeatableThread, one of whose member variables is of type
|
||||
// InstrumentedCondVar. The callback for
|
||||
// InstrumentedCondVar::TimedWaitInternal can be triggered by stats dumping
|
||||
// and persisting threads and cause time_spent_deleting measurement to become
|
||||
// incorrect.
|
||||
options.stats_dump_period_sec = 0;
|
||||
options.stats_persist_period_sec = 0;
|
||||
options.env = env_;
|
||||
|
||||
int64_t rate_bytes_per_sec = 1024 * 10; // 10 Kbs / Sec
|
||||
|
||||
@@ -2309,6 +2309,42 @@ TEST_F(DBTest, ReadonlyDBGetLiveManifestSize) {
|
||||
Close();
|
||||
} while (ChangeCompactOptions());
|
||||
}
|
||||
|
||||
TEST_F(DBTest, GetLiveBlobFiles) {
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
// Add a live blob file.
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr uint64_t blob_file_number = 234;
|
||||
constexpr uint64_t total_blob_count = 555;
|
||||
constexpr uint64_t total_blob_bytes = 66666;
|
||||
constexpr char checksum_method[] = "CRC32";
|
||||
constexpr char checksum_value[] = "3d87ff57";
|
||||
|
||||
edit.AddBlobFile(blob_file_number, total_blob_count, total_blob_bytes,
|
||||
checksum_method, checksum_value);
|
||||
|
||||
dbfull()->TEST_LockMutex();
|
||||
Status s = versions->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, dbfull()->mutex());
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Make sure it appears in the results returned by GetLiveFiles.
|
||||
uint64_t manifest_size = 0;
|
||||
std::vector<std::string> files;
|
||||
ASSERT_OK(dbfull()->GetLiveFiles(files, &manifest_size));
|
||||
|
||||
ASSERT_FALSE(files.empty());
|
||||
ASSERT_EQ(files[0], BlobFileName("", blob_file_number));
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_F(DBTest, PurgeInfoLogs) {
|
||||
|
||||
@@ -4328,6 +4328,24 @@ TEST_F(DBTest2, SameSmallestInSameLevel) {
|
||||
ASSERT_EQ("2,3,4,5,6,7,8", Get("key"));
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, FileConsistencyCheckInOpen) {
|
||||
Put("foo", "bar");
|
||||
Flush();
|
||||
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"VersionBuilder::CheckConsistencyBeforeReturn", [&](void* arg) {
|
||||
Status* ret_s = static_cast<Status*>(arg);
|
||||
*ret_s = Status::Corruption("fcc");
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
Options options = CurrentOptions();
|
||||
options.force_consistency_checks = true;
|
||||
ASSERT_NOK(TryReopen(options));
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
}
|
||||
|
||||
TEST_F(DBTest2, BlockBasedTablePrefixIndexSeekForPrev) {
|
||||
// create a DB with block prefix index
|
||||
BlockBasedTableOptions table_options;
|
||||
|
||||
@@ -14,10 +14,19 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
|
||||
namespace {
|
||||
int64_t MaybeCurrentTime(Env* env) {
|
||||
int64_t time = 1337346000; // arbitrary fallback default
|
||||
(void)env->GetCurrentTime(&time);
|
||||
return time;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Special Env used to delay background operations
|
||||
|
||||
SpecialEnv::SpecialEnv(Env* base)
|
||||
: EnvWrapper(base),
|
||||
maybe_starting_time_(MaybeCurrentTime(base)),
|
||||
rnd_(301),
|
||||
sleep_counter_(this),
|
||||
addon_time_(0),
|
||||
|
||||
+19
-1
@@ -502,10 +502,14 @@ class SpecialEnv : public EnvWrapper {
|
||||
|
||||
virtual Status GetCurrentTime(int64_t* unix_time) override {
|
||||
Status s;
|
||||
if (!time_elapse_only_sleep_) {
|
||||
if (time_elapse_only_sleep_) {
|
||||
*unix_time = maybe_starting_time_;
|
||||
} else {
|
||||
s = target()->GetCurrentTime(unix_time);
|
||||
}
|
||||
if (s.ok()) {
|
||||
// FIXME: addon_time_ sometimes used to mean seconds (here) and
|
||||
// sometimes microseconds
|
||||
*unix_time += addon_time_.load();
|
||||
}
|
||||
return s;
|
||||
@@ -531,6 +535,20 @@ class SpecialEnv : public EnvWrapper {
|
||||
return target()->DeleteFile(fname);
|
||||
}
|
||||
|
||||
void SetTimeElapseOnlySleep(Options* options) {
|
||||
time_elapse_only_sleep_ = true;
|
||||
no_slowdown_ = true;
|
||||
// Need to disable stats dumping and persisting which also use
|
||||
// RepeatableThread, which uses InstrumentedCondVar::TimedWaitInternal.
|
||||
// With time_elapse_only_sleep_, this can hang on some platforms.
|
||||
// TODO: why? investigate/fix
|
||||
options->stats_dump_period_sec = 0;
|
||||
options->stats_persist_period_sec = 0;
|
||||
}
|
||||
|
||||
// Something to return when mocking current time
|
||||
const int64_t maybe_starting_time_;
|
||||
|
||||
Random rnd_;
|
||||
port::Mutex rnd_mutex_; // Lock to pretect rnd_
|
||||
|
||||
|
||||
+5
-2
@@ -1422,8 +1422,11 @@ TEST_F(DBWALTest, RecoverFromCorruptedWALWithoutFlush) {
|
||||
delete iter;
|
||||
return data;
|
||||
};
|
||||
for (auto& mode : wal_recovery_mode_string_map) {
|
||||
options.wal_recovery_mode = mode.second;
|
||||
for (auto& mode : {WALRecoveryMode::kTolerateCorruptedTailRecords,
|
||||
WALRecoveryMode::kAbsoluteConsistency,
|
||||
WALRecoveryMode::kPointInTimeRecovery,
|
||||
WALRecoveryMode::kSkipAnyCorruptedRecords}) {
|
||||
options.wal_recovery_mode = mode;
|
||||
for (auto trunc : {true, false}) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
for (int j = jstart; j < jend; j++) {
|
||||
|
||||
+5
-2
@@ -141,11 +141,14 @@ struct JobContext {
|
||||
std::vector<CandidateFileInfo> full_scan_candidate_files;
|
||||
|
||||
// the list of all live sst files that cannot be deleted
|
||||
std::vector<FileDescriptor> sst_live;
|
||||
std::vector<uint64_t> sst_live;
|
||||
|
||||
// a list of sst files that we need to delete
|
||||
// the list of sst files that we need to delete
|
||||
std::vector<ObsoleteFileInfo> sst_delete_files;
|
||||
|
||||
// the list of all live blob files that cannot be deleted
|
||||
std::vector<uint64_t> blob_live;
|
||||
|
||||
// the list of blob files that we need to delete
|
||||
std::vector<ObsoleteBlobFileInfo> blob_delete_files;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -193,6 +194,124 @@ TEST_F(ObsoleteFilesTest, DeleteObsoleteOptionsFile) {
|
||||
ASSERT_EQ(2, opts_file_count);
|
||||
}
|
||||
|
||||
TEST_F(ObsoleteFilesTest, BlobFiles) {
|
||||
VersionSet* const versions = dbfull()->TEST_GetVersionSet();
|
||||
assert(versions);
|
||||
assert(versions->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
// Add a blob file that consists of nothing but garbage (and is thus obsolete)
|
||||
// and another one that is live.
|
||||
VersionEdit edit;
|
||||
|
||||
constexpr uint64_t first_blob_file_number = 234;
|
||||
constexpr uint64_t first_total_blob_count = 555;
|
||||
constexpr uint64_t first_total_blob_bytes = 66666;
|
||||
constexpr char first_checksum_method[] = "CRC32";
|
||||
constexpr char first_checksum_value[] = "3d87ff57";
|
||||
|
||||
edit.AddBlobFile(first_blob_file_number, first_total_blob_count,
|
||||
first_total_blob_bytes, first_checksum_method,
|
||||
first_checksum_value);
|
||||
edit.AddBlobFileGarbage(first_blob_file_number, first_total_blob_count,
|
||||
first_total_blob_bytes);
|
||||
|
||||
constexpr uint64_t second_blob_file_number = 456;
|
||||
constexpr uint64_t second_total_blob_count = 100;
|
||||
constexpr uint64_t second_total_blob_bytes = 2000000;
|
||||
constexpr char second_checksum_method[] = "CRC32B";
|
||||
constexpr char second_checksum_value[] = "6dbdf23a";
|
||||
edit.AddBlobFile(second_blob_file_number, second_total_blob_count,
|
||||
second_total_blob_bytes, second_checksum_method,
|
||||
second_checksum_value);
|
||||
|
||||
dbfull()->TEST_LockMutex();
|
||||
Status s = versions->LogAndApply(cfd, *cfd->GetLatestMutableCFOptions(),
|
||||
&edit, dbfull()->mutex());
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Check for obsolete files and make sure the first blob file is picked up
|
||||
// and grabbed for purge. The second blob file should be on the live list.
|
||||
constexpr int job_id = 0;
|
||||
JobContext job_context{job_id};
|
||||
|
||||
dbfull()->TEST_LockMutex();
|
||||
constexpr bool force_full_scan = false;
|
||||
dbfull()->FindObsoleteFiles(&job_context, force_full_scan);
|
||||
dbfull()->TEST_UnlockMutex();
|
||||
|
||||
ASSERT_TRUE(job_context.HaveSomethingToDelete());
|
||||
ASSERT_EQ(job_context.blob_delete_files.size(), 1);
|
||||
ASSERT_EQ(job_context.blob_delete_files[0].GetBlobFileNumber(),
|
||||
first_blob_file_number);
|
||||
|
||||
const auto& files_grabbed_for_purge =
|
||||
dbfull()->TEST_GetFilesGrabbedForPurge();
|
||||
ASSERT_NE(files_grabbed_for_purge.find(first_blob_file_number),
|
||||
files_grabbed_for_purge.end());
|
||||
|
||||
ASSERT_EQ(job_context.blob_live.size(), 1);
|
||||
ASSERT_EQ(job_context.blob_live[0], second_blob_file_number);
|
||||
|
||||
// Hack the job context a bit by adding a few files to the full scan
|
||||
// list and adjusting the pending file number. We add the two files
|
||||
// above as well as two additional ones, where one is old
|
||||
// and should be cleaned up, and the other is still pending.
|
||||
assert(cfd->ioptions());
|
||||
assert(!cfd->ioptions()->cf_paths.empty());
|
||||
const std::string& path = cfd->ioptions()->cf_paths.front().path;
|
||||
|
||||
constexpr uint64_t old_blob_file_number = 123;
|
||||
constexpr uint64_t pending_blob_file_number = 567;
|
||||
|
||||
job_context.full_scan_candidate_files.emplace_back(
|
||||
BlobFileName(old_blob_file_number), path);
|
||||
job_context.full_scan_candidate_files.emplace_back(
|
||||
BlobFileName(first_blob_file_number), path);
|
||||
job_context.full_scan_candidate_files.emplace_back(
|
||||
BlobFileName(second_blob_file_number), path);
|
||||
job_context.full_scan_candidate_files.emplace_back(
|
||||
BlobFileName(pending_blob_file_number), path);
|
||||
|
||||
job_context.min_pending_output = pending_blob_file_number;
|
||||
|
||||
// Purge obsolete files and make sure we purge the old file and the first file
|
||||
// (and keep the second file and the pending file).
|
||||
std::vector<std::string> deleted_files;
|
||||
SyncPoint::GetInstance()->SetCallBack(
|
||||
"DBImpl::DeleteObsoleteFileImpl::BeforeDeletion", [&](void* arg) {
|
||||
const std::string* file = static_cast<std::string*>(arg);
|
||||
assert(file);
|
||||
|
||||
constexpr char blob_extension[] = ".blob";
|
||||
|
||||
if (file->find(blob_extension) != std::string::npos) {
|
||||
deleted_files.emplace_back(*file);
|
||||
}
|
||||
});
|
||||
SyncPoint::GetInstance()->EnableProcessing();
|
||||
|
||||
dbfull()->PurgeObsoleteFiles(job_context);
|
||||
job_context.Clean();
|
||||
|
||||
SyncPoint::GetInstance()->DisableProcessing();
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
|
||||
ASSERT_EQ(files_grabbed_for_purge.find(first_blob_file_number),
|
||||
files_grabbed_for_purge.end());
|
||||
|
||||
std::sort(deleted_files.begin(), deleted_files.end());
|
||||
const std::vector<std::string> expected_deleted_files{
|
||||
BlobFileName(path, old_blob_file_number),
|
||||
BlobFileName(path, first_blob_file_number)};
|
||||
|
||||
ASSERT_EQ(deleted_files, expected_deleted_files);
|
||||
}
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
#ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS
|
||||
|
||||
+16
-6
@@ -236,11 +236,11 @@ class VersionBuilder::Rep {
|
||||
|
||||
auto f1 = level_files[i - 1];
|
||||
auto f2 = level_files[i];
|
||||
#ifndef NDEBUG
|
||||
auto pair = std::make_pair(&f1, &f2);
|
||||
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistency", &pair);
|
||||
#endif
|
||||
if (level == 0) {
|
||||
#ifndef NDEBUG
|
||||
auto pair = std::make_pair(&f1, &f2);
|
||||
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistency0", &pair);
|
||||
#endif
|
||||
if (!level_zero_cmp_(f1, f2)) {
|
||||
fprintf(stderr, "L0 files are not sorted properly");
|
||||
return Status::Corruption("L0 files are not sorted properly");
|
||||
@@ -279,6 +279,10 @@ class VersionBuilder::Rep {
|
||||
NumberToString(f2->fd.GetNumber()));
|
||||
}
|
||||
} else {
|
||||
#ifndef NDEBUG
|
||||
auto pair = std::make_pair(&f1, &f2);
|
||||
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistency1", &pair);
|
||||
#endif
|
||||
if (!level_nonzero_cmp_(f1, f2)) {
|
||||
fprintf(stderr, "L%d files are not sorted properly", level);
|
||||
return Status::Corruption("L" + NumberToString(level) +
|
||||
@@ -316,7 +320,10 @@ class VersionBuilder::Rep {
|
||||
}
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
Status ret_s;
|
||||
TEST_SYNC_POINT_CALLBACK("VersionBuilder::CheckConsistencyBeforeReturn",
|
||||
&ret_s);
|
||||
return ret_s;
|
||||
}
|
||||
|
||||
Status CheckConsistencyForDeletes(VersionEdit* /*edit*/, uint64_t number,
|
||||
@@ -463,7 +470,10 @@ class VersionBuilder::Rep {
|
||||
const auto number = del_file.second;
|
||||
if (level < num_levels_) {
|
||||
levels_[level].deleted_files.insert(number);
|
||||
CheckConsistencyForDeletes(edit, number, level);
|
||||
s = CheckConsistencyForDeletes(edit, number, level);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
|
||||
auto exising = levels_[level].added_files.find(number);
|
||||
if (exising != levels_[level].added_files.end()) {
|
||||
|
||||
@@ -839,6 +839,36 @@ TEST_F(VersionBuilderTest, CheckConsistencyForBlobFilesAllGarbage) {
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, CheckConsistencyForFileDeletedTwice) {
|
||||
Add(0, 1U, "150", "200", 100U);
|
||||
UpdateVersionStorageInfo();
|
||||
|
||||
VersionEdit version_edit;
|
||||
version_edit.DeleteFile(0, 1U);
|
||||
|
||||
EnvOptions env_options;
|
||||
constexpr TableCache* table_cache = nullptr;
|
||||
constexpr VersionSet* version_set = nullptr;
|
||||
|
||||
VersionBuilder version_builder(env_options, &ioptions_, table_cache,
|
||||
&vstorage_, version_set);
|
||||
VersionStorageInfo new_vstorage(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr,
|
||||
true /* force_consistency_checks */);
|
||||
ASSERT_OK(version_builder.Apply(&version_edit));
|
||||
ASSERT_OK(version_builder.SaveTo(&new_vstorage));
|
||||
|
||||
VersionBuilder version_builder2(env_options, &ioptions_, table_cache,
|
||||
&new_vstorage, version_set);
|
||||
VersionStorageInfo new_vstorage2(&icmp_, ucmp_, options_.num_levels,
|
||||
kCompactionStyleLevel, nullptr,
|
||||
true /* force_consistency_checks */);
|
||||
ASSERT_NOK(version_builder2.Apply(&version_edit));
|
||||
|
||||
UnrefFilesInVersion(&new_vstorage);
|
||||
UnrefFilesInVersion(&new_vstorage2);
|
||||
}
|
||||
|
||||
TEST_F(VersionBuilderTest, EstimatedActiveKeys) {
|
||||
const uint32_t kTotalSamples = 20;
|
||||
const uint32_t kNumLevels = 5;
|
||||
|
||||
+25
-15
@@ -377,6 +377,7 @@ Status VersionEditHandler::MaybeCreateVersion(const VersionEdit& /*edit*/,
|
||||
ColumnFamilyData* cfd,
|
||||
bool force_create_version) {
|
||||
assert(cfd->initialized());
|
||||
Status s;
|
||||
if (force_create_version) {
|
||||
auto builder_iter = builders_.find(cfd->GetID());
|
||||
assert(builder_iter != builders_.end());
|
||||
@@ -384,13 +385,18 @@ Status VersionEditHandler::MaybeCreateVersion(const VersionEdit& /*edit*/,
|
||||
auto* v = new Version(cfd, version_set_, version_set_->file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
version_set_->current_version_number_++);
|
||||
builder->SaveTo(v->storage_info());
|
||||
// Install new version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
!(version_set_->db_options_->skip_stats_update_on_db_open));
|
||||
version_set_->AppendVersion(cfd, v);
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
if (s.ok()) {
|
||||
// Install new version
|
||||
v->PrepareApply(
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
!(version_set_->db_options_->skip_stats_update_on_db_open));
|
||||
version_set_->AppendVersion(cfd, v);
|
||||
} else {
|
||||
delete v;
|
||||
}
|
||||
}
|
||||
return Status::OK();
|
||||
return s;
|
||||
}
|
||||
|
||||
Status VersionEditHandler::LoadTables(ColumnFamilyData* cfd,
|
||||
@@ -558,16 +564,20 @@ Status VersionEditHandlerPointInTime::MaybeCreateVersion(
|
||||
auto* version = new Version(cfd, version_set_, version_set_->file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
version_set_->current_version_number_++);
|
||||
builder->SaveTo(version->storage_info());
|
||||
version->PrepareApply(
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
!version_set_->db_options_->skip_stats_update_on_db_open);
|
||||
auto v_iter = versions_.find(cfd->GetID());
|
||||
if (v_iter != versions_.end()) {
|
||||
delete v_iter->second;
|
||||
v_iter->second = version;
|
||||
s = builder->SaveTo(version->storage_info());
|
||||
if (s.ok()) {
|
||||
version->PrepareApply(
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
!version_set_->db_options_->skip_stats_update_on_db_open);
|
||||
auto v_iter = versions_.find(cfd->GetID());
|
||||
if (v_iter != versions_.end()) {
|
||||
delete v_iter->second;
|
||||
v_iter->second = version;
|
||||
} else {
|
||||
versions_.emplace(cfd->GetID(), version);
|
||||
}
|
||||
} else {
|
||||
versions_.emplace(cfd->GetID(), version);
|
||||
delete version;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
|
||||
+109
-45
@@ -3414,13 +3414,27 @@ bool VersionStorageInfo::RangeMightExistAfterSortedRun(
|
||||
return false;
|
||||
}
|
||||
|
||||
void Version::AddLiveFiles(std::vector<FileDescriptor>* live) {
|
||||
for (int level = 0; level < storage_info_.num_levels(); level++) {
|
||||
const std::vector<FileMetaData*>& files = storage_info_.files_[level];
|
||||
for (const auto& file : files) {
|
||||
live->push_back(file->fd);
|
||||
void Version::AddLiveFiles(std::vector<uint64_t>* live_table_files,
|
||||
std::vector<uint64_t>* live_blob_files) const {
|
||||
assert(live_table_files);
|
||||
assert(live_blob_files);
|
||||
|
||||
for (int level = 0; level < storage_info_.num_levels(); ++level) {
|
||||
const auto& level_files = storage_info_.LevelFiles(level);
|
||||
for (const auto& meta : level_files) {
|
||||
assert(meta);
|
||||
|
||||
live_table_files->emplace_back(meta->fd.GetNumber());
|
||||
}
|
||||
}
|
||||
|
||||
const auto& blob_files = storage_info_.GetBlobFiles();
|
||||
for (const auto& pair : blob_files) {
|
||||
const auto& meta = pair.second;
|
||||
assert(meta);
|
||||
|
||||
live_blob_files->emplace_back(meta->GetBlobFileNumber());
|
||||
}
|
||||
}
|
||||
|
||||
std::string Version::DebugString(bool hex, bool print_stats) const {
|
||||
@@ -4594,7 +4608,11 @@ Status VersionSet::Recover(
|
||||
Version* v = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
builder->SaveTo(v->storage_info());
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
if (!s.ok()) {
|
||||
delete v;
|
||||
return s;
|
||||
}
|
||||
|
||||
// Install recovered version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
@@ -5145,7 +5163,7 @@ Status VersionSet::DumpManifest(Options& options, std::string& dscname,
|
||||
Version* v = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
builder->SaveTo(v->storage_info());
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(), false);
|
||||
|
||||
printf("--------------- Column family \"%s\" (ID %" PRIu32
|
||||
@@ -5507,44 +5525,70 @@ uint64_t VersionSet::ApproximateSize(Version* v, const FdWithKeyRange& f,
|
||||
v->GetMutableCFOptions().prefix_extractor.get());
|
||||
}
|
||||
|
||||
void VersionSet::AddLiveFiles(std::vector<FileDescriptor>* live_list) {
|
||||
void VersionSet::AddLiveFiles(std::vector<uint64_t>* live_table_files,
|
||||
std::vector<uint64_t>* live_blob_files) const {
|
||||
assert(live_table_files);
|
||||
assert(live_blob_files);
|
||||
|
||||
// pre-calculate space requirement
|
||||
int64_t total_files = 0;
|
||||
size_t total_table_files = 0;
|
||||
size_t total_blob_files = 0;
|
||||
|
||||
assert(column_family_set_);
|
||||
for (auto cfd : *column_family_set_) {
|
||||
assert(cfd);
|
||||
|
||||
if (!cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
Version* dummy_versions = cfd->dummy_versions();
|
||||
|
||||
Version* const dummy_versions = cfd->dummy_versions();
|
||||
assert(dummy_versions);
|
||||
|
||||
for (Version* v = dummy_versions->next_; v != dummy_versions;
|
||||
v = v->next_) {
|
||||
assert(v);
|
||||
|
||||
const auto* vstorage = v->storage_info();
|
||||
for (int level = 0; level < vstorage->num_levels(); level++) {
|
||||
total_files += vstorage->LevelFiles(level).size();
|
||||
assert(vstorage);
|
||||
|
||||
for (int level = 0; level < vstorage->num_levels(); ++level) {
|
||||
total_table_files += vstorage->LevelFiles(level).size();
|
||||
}
|
||||
|
||||
total_blob_files += vstorage->GetBlobFiles().size();
|
||||
}
|
||||
}
|
||||
|
||||
// just one time extension to the right size
|
||||
live_list->reserve(live_list->size() + static_cast<size_t>(total_files));
|
||||
live_table_files->reserve(live_table_files->size() + total_table_files);
|
||||
live_blob_files->reserve(live_blob_files->size() + total_blob_files);
|
||||
|
||||
assert(column_family_set_);
|
||||
for (auto cfd : *column_family_set_) {
|
||||
assert(cfd);
|
||||
if (!cfd->initialized()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto* current = cfd->current();
|
||||
bool found_current = false;
|
||||
Version* dummy_versions = cfd->dummy_versions();
|
||||
|
||||
Version* const dummy_versions = cfd->dummy_versions();
|
||||
assert(dummy_versions);
|
||||
|
||||
for (Version* v = dummy_versions->next_; v != dummy_versions;
|
||||
v = v->next_) {
|
||||
v->AddLiveFiles(live_list);
|
||||
v->AddLiveFiles(live_table_files, live_blob_files);
|
||||
if (v == current) {
|
||||
found_current = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found_current && current != nullptr) {
|
||||
// Should never happen unless it is a bug.
|
||||
assert(false);
|
||||
current->AddLiveFiles(live_list);
|
||||
current->AddLiveFiles(live_table_files, live_blob_files);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5966,13 +6010,23 @@ Status ReactiveVersionSet::Recover(
|
||||
Version* v = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
builder->SaveTo(v->storage_info());
|
||||
s = builder->SaveTo(v->storage_info());
|
||||
|
||||
// Install recovered version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
!(db_options_->skip_stats_update_on_db_open));
|
||||
AppendVersion(cfd, v);
|
||||
if (s.ok()) {
|
||||
// Install recovered version
|
||||
v->PrepareApply(*cfd->GetLatestMutableCFOptions(),
|
||||
!(db_options_->skip_stats_update_on_db_open));
|
||||
AppendVersion(cfd, v);
|
||||
} else {
|
||||
ROCKS_LOG_ERROR(db_options_->info_log,
|
||||
"[%s]: inconsistent version: %s\n",
|
||||
cfd->GetName().c_str(), s.ToString().c_str());
|
||||
delete v;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
next_file_number_.store(version_edit.next_file_number_ + 1);
|
||||
last_allocated_sequence_ = version_edit.last_sequence_;
|
||||
last_published_sequence_ = version_edit.last_sequence_;
|
||||
@@ -6049,6 +6103,8 @@ Status ReactiveVersionSet::ReadAndApply(
|
||||
s = ApplyOneVersionEditToBuilder(edit, cfds_changed, &temp_edit);
|
||||
if (s.ok()) {
|
||||
applied_edits++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6058,13 +6114,14 @@ Status ReactiveVersionSet::ReadAndApply(
|
||||
}
|
||||
// It's possible that:
|
||||
// 1) s.IsCorruption(), indicating the current MANIFEST is corrupted.
|
||||
// Or the version(s) rebuilt from tailing the MANIFEST is inconsistent.
|
||||
// 2) we have finished reading the current MANIFEST.
|
||||
// 3) we have encountered an IOError reading the current MANIFEST.
|
||||
// We need to look for the next MANIFEST and start from there. If we cannot
|
||||
// find the next MANIFEST, we should exit the loop.
|
||||
s = MaybeSwitchManifest(reader->GetReporter(), manifest_reader);
|
||||
Status tmp_s = MaybeSwitchManifest(reader->GetReporter(), manifest_reader);
|
||||
reader = manifest_reader->get();
|
||||
if (s.ok()) {
|
||||
if (tmp_s.ok()) {
|
||||
if (reader->file()->file_name() == old_manifest_path) {
|
||||
// Still processing the same MANIFEST, thus no need to continue this
|
||||
// loop since no record is available if we have reached here.
|
||||
@@ -6094,6 +6151,7 @@ Status ReactiveVersionSet::ReadAndApply(
|
||||
number_of_edits_to_skip_ += 2;
|
||||
}
|
||||
}
|
||||
s = tmp_s;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6186,12 +6244,16 @@ Status ReactiveVersionSet::ApplyOneVersionEditToBuilder(
|
||||
auto version = new Version(cfd, this, file_options_,
|
||||
*cfd->GetLatestMutableCFOptions(),
|
||||
current_version_number_++);
|
||||
builder->SaveTo(version->storage_info());
|
||||
version->PrepareApply(*cfd->GetLatestMutableCFOptions(), true);
|
||||
AppendVersion(cfd, version);
|
||||
active_version_builders_.erase(builder_iter);
|
||||
if (cfds_changed->count(cfd) == 0) {
|
||||
cfds_changed->insert(cfd);
|
||||
s = builder->SaveTo(version->storage_info());
|
||||
if (s.ok()) {
|
||||
version->PrepareApply(*cfd->GetLatestMutableCFOptions(), true);
|
||||
AppendVersion(cfd, version);
|
||||
active_version_builders_.erase(builder_iter);
|
||||
if (cfds_changed->count(cfd) == 0) {
|
||||
cfds_changed->insert(cfd);
|
||||
}
|
||||
} else {
|
||||
delete version;
|
||||
}
|
||||
} else if (s.IsPathNotFound()) {
|
||||
s = Status::OK();
|
||||
@@ -6199,23 +6261,25 @@ Status ReactiveVersionSet::ApplyOneVersionEditToBuilder(
|
||||
// Some other error has occurred during LoadTableHandlers.
|
||||
}
|
||||
|
||||
if (version_edit->HasNextFile()) {
|
||||
next_file_number_.store(version_edit->next_file_number_ + 1);
|
||||
if (s.ok()) {
|
||||
if (version_edit->HasNextFile()) {
|
||||
next_file_number_.store(version_edit->next_file_number_ + 1);
|
||||
}
|
||||
if (version_edit->has_last_sequence_) {
|
||||
last_allocated_sequence_ = version_edit->last_sequence_;
|
||||
last_published_sequence_ = version_edit->last_sequence_;
|
||||
last_sequence_ = version_edit->last_sequence_;
|
||||
}
|
||||
if (version_edit->has_prev_log_number_) {
|
||||
prev_log_number_ = version_edit->prev_log_number_;
|
||||
MarkFileNumberUsed(version_edit->prev_log_number_);
|
||||
}
|
||||
if (version_edit->has_log_number_) {
|
||||
MarkFileNumberUsed(version_edit->log_number_);
|
||||
}
|
||||
column_family_set_->UpdateMaxColumnFamily(version_edit->max_column_family_);
|
||||
MarkMinLogNumberToKeep2PC(version_edit->min_log_number_to_keep_);
|
||||
}
|
||||
if (version_edit->has_last_sequence_) {
|
||||
last_allocated_sequence_ = version_edit->last_sequence_;
|
||||
last_published_sequence_ = version_edit->last_sequence_;
|
||||
last_sequence_ = version_edit->last_sequence_;
|
||||
}
|
||||
if (version_edit->has_prev_log_number_) {
|
||||
prev_log_number_ = version_edit->prev_log_number_;
|
||||
MarkFileNumberUsed(version_edit->prev_log_number_);
|
||||
}
|
||||
if (version_edit->has_log_number_) {
|
||||
MarkFileNumberUsed(version_edit->log_number_);
|
||||
}
|
||||
column_family_set_->UpdateMaxColumnFamily(version_edit->max_column_family_);
|
||||
MarkMinLogNumberToKeep2PC(version_edit->min_log_number_to_keep_);
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
+8
-4
@@ -629,8 +629,10 @@ class Version {
|
||||
// and return true. Otherwise, return false.
|
||||
bool Unref();
|
||||
|
||||
// Add all files listed in the current version to *live.
|
||||
void AddLiveFiles(std::vector<FileDescriptor>* live);
|
||||
// Add all files listed in the current version to *live_table_files and
|
||||
// *live_blob_files.
|
||||
void AddLiveFiles(std::vector<uint64_t>* live_table_files,
|
||||
std::vector<uint64_t>* live_blob_files) const;
|
||||
|
||||
// Return a human readable string that describes this version's contents.
|
||||
std::string DebugString(bool hex = false, bool print_stats = false) const;
|
||||
@@ -1047,8 +1049,10 @@ class VersionSet {
|
||||
const Compaction* c, RangeDelAggregator* range_del_agg,
|
||||
const FileOptions& file_options_compactions);
|
||||
|
||||
// Add all files listed in any live version to *live.
|
||||
void AddLiveFiles(std::vector<FileDescriptor>* live_list);
|
||||
// Add all files listed in any live version to *live_table_files and
|
||||
// *live_blob_files. Note that these lists may contain duplicates.
|
||||
void AddLiveFiles(std::vector<uint64_t>* live_table_files,
|
||||
std::vector<uint64_t>* live_blob_files) const;
|
||||
|
||||
// Return the approximate size of data to be scanned for range [start, end)
|
||||
// in levels [start_level, end_level). If end_level == -1 it will search
|
||||
|
||||
+80
-2
@@ -960,6 +960,84 @@ TEST_F(VersionSetTest, PersistBlobFileStateInNewManifest) {
|
||||
SyncPoint::GetInstance()->ClearAllCallBacks();
|
||||
}
|
||||
|
||||
TEST_F(VersionSetTest, AddLiveBlobFiles) {
|
||||
// Initialize the database and add a blob file.
|
||||
NewDB();
|
||||
|
||||
assert(versions_);
|
||||
assert(versions_->GetColumnFamilySet());
|
||||
|
||||
ColumnFamilyData* const cfd = versions_->GetColumnFamilySet()->GetDefault();
|
||||
assert(cfd);
|
||||
|
||||
VersionEdit first;
|
||||
|
||||
constexpr uint64_t first_blob_file_number = 234;
|
||||
constexpr uint64_t first_total_blob_count = 555;
|
||||
constexpr uint64_t first_total_blob_bytes = 66666;
|
||||
constexpr char first_checksum_method[] = "CRC32";
|
||||
constexpr char first_checksum_value[] = "3d87ff57";
|
||||
|
||||
first.AddBlobFile(first_blob_file_number, first_total_blob_count,
|
||||
first_total_blob_bytes, first_checksum_method,
|
||||
first_checksum_value);
|
||||
|
||||
mutex_.Lock();
|
||||
Status s = versions_->LogAndApply(cfd, mutable_cf_options_, &first, &mutex_);
|
||||
mutex_.Unlock();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Reference the version so it stays alive even after the following version
|
||||
// edit.
|
||||
Version* const version = cfd->current();
|
||||
assert(version);
|
||||
|
||||
version->Ref();
|
||||
|
||||
// Get live files directly from version.
|
||||
std::vector<uint64_t> version_table_files;
|
||||
std::vector<uint64_t> version_blob_files;
|
||||
|
||||
version->AddLiveFiles(&version_table_files, &version_blob_files);
|
||||
|
||||
ASSERT_EQ(version_blob_files.size(), 1);
|
||||
ASSERT_EQ(version_blob_files[0], first_blob_file_number);
|
||||
|
||||
// Add another blob file.
|
||||
VersionEdit second;
|
||||
|
||||
constexpr uint64_t second_blob_file_number = 456;
|
||||
constexpr uint64_t second_total_blob_count = 100;
|
||||
constexpr uint64_t second_total_blob_bytes = 2000000;
|
||||
constexpr char second_checksum_method[] = "CRC32B";
|
||||
constexpr char second_checksum_value[] = "6dbdf23a";
|
||||
second.AddBlobFile(second_blob_file_number, second_total_blob_count,
|
||||
second_total_blob_bytes, second_checksum_method,
|
||||
second_checksum_value);
|
||||
|
||||
mutex_.Lock();
|
||||
s = versions_->LogAndApply(cfd, mutable_cf_options_, &second, &mutex_);
|
||||
mutex_.Unlock();
|
||||
|
||||
ASSERT_OK(s);
|
||||
|
||||
// Get all live files from version set. Note that the result contains
|
||||
// duplicates.
|
||||
std::vector<uint64_t> all_table_files;
|
||||
std::vector<uint64_t> all_blob_files;
|
||||
|
||||
versions_->AddLiveFiles(&all_table_files, &all_blob_files);
|
||||
|
||||
ASSERT_EQ(all_blob_files.size(), 3);
|
||||
ASSERT_EQ(all_blob_files[0], first_blob_file_number);
|
||||
ASSERT_EQ(all_blob_files[1], first_blob_file_number);
|
||||
ASSERT_EQ(all_blob_files[2], second_blob_file_number);
|
||||
|
||||
// Clean up previous version.
|
||||
version->Unref();
|
||||
}
|
||||
|
||||
TEST_F(VersionSetTest, ObsoleteBlobFile) {
|
||||
// Initialize the database and add a blob file (with no garbage just yet).
|
||||
NewDB();
|
||||
@@ -1370,7 +1448,7 @@ TEST_F(VersionSetAtomicGroupTest,
|
||||
// Write the corrupted edits.
|
||||
AddNewEditsToLog(kAtomicGroupSize);
|
||||
mu.Lock();
|
||||
EXPECT_OK(
|
||||
EXPECT_NOK(
|
||||
reactive_versions_->ReadAndApply(&mu, &manifest_reader, &cfds_changed));
|
||||
mu.Unlock();
|
||||
EXPECT_EQ(edits_[kAtomicGroupSize / 2].DebugString(),
|
||||
@@ -1420,7 +1498,7 @@ TEST_F(VersionSetAtomicGroupTest,
|
||||
&manifest_reader_status));
|
||||
AddNewEditsToLog(kAtomicGroupSize);
|
||||
mu.Lock();
|
||||
EXPECT_OK(
|
||||
EXPECT_NOK(
|
||||
reactive_versions_->ReadAndApply(&mu, &manifest_reader, &cfds_changed));
|
||||
mu.Unlock();
|
||||
EXPECT_EQ(edits_[1].DebugString(),
|
||||
|
||||
@@ -1630,6 +1630,15 @@ class MemTableInserter : public WriteBatch::Handler {
|
||||
cfd->ioptions()->table_factory->Name() + " in CF " +
|
||||
cfd->GetName());
|
||||
}
|
||||
int cmp = cfd->user_comparator()->Compare(begin_key, end_key);
|
||||
if (cmp > 0) {
|
||||
// It's an empty range where endpoints appear mistaken. Don't bother
|
||||
// applying it to the DB, and return an error to the user.
|
||||
return Status::InvalidArgument("end key comes before start key");
|
||||
} else if (cmp == 0) {
|
||||
// It's an empty range. Don't bother applying it to the DB.
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
auto ret_status =
|
||||
|
||||
@@ -112,6 +112,7 @@ DECLARE_bool(memtable_whole_key_filtering);
|
||||
DECLARE_int32(open_files);
|
||||
DECLARE_int64(compressed_cache_size);
|
||||
DECLARE_int32(compaction_style);
|
||||
DECLARE_int32(num_levels);
|
||||
DECLARE_int32(level0_file_num_compaction_trigger);
|
||||
DECLARE_int32(level0_slowdown_writes_trigger);
|
||||
DECLARE_int32(level0_stop_writes_trigger);
|
||||
|
||||
@@ -186,6 +186,9 @@ DEFINE_int64(compressed_cache_size, -1,
|
||||
DEFINE_int32(compaction_style, ROCKSDB_NAMESPACE::Options().compaction_style,
|
||||
"");
|
||||
|
||||
DEFINE_int32(num_levels, ROCKSDB_NAMESPACE::Options().num_levels,
|
||||
"Number of levels in the DB");
|
||||
|
||||
DEFINE_int32(level0_file_num_compaction_trigger,
|
||||
ROCKSDB_NAMESPACE::Options().level0_file_num_compaction_trigger,
|
||||
"Level0 compaction start trigger");
|
||||
|
||||
@@ -79,6 +79,11 @@ std::string LogFileName(uint64_t number) {
|
||||
return MakeFileName(number, "log");
|
||||
}
|
||||
|
||||
std::string BlobFileName(uint64_t number) {
|
||||
assert(number > 0);
|
||||
return MakeFileName(number, kRocksDBBlobFileExt.c_str());
|
||||
}
|
||||
|
||||
std::string BlobFileName(const std::string& blobdirname, uint64_t number) {
|
||||
assert(number > 0);
|
||||
return MakeFileName(blobdirname, number, kRocksDBBlobFileExt.c_str());
|
||||
|
||||
@@ -56,6 +56,8 @@ extern std::string LogFileName(const std::string& dbname, uint64_t number);
|
||||
|
||||
extern std::string LogFileName(uint64_t number);
|
||||
|
||||
extern std::string BlobFileName(uint64_t number);
|
||||
|
||||
extern std::string BlobFileName(const std::string& bdirname, uint64_t number);
|
||||
|
||||
extern std::string BlobFileName(const std::string& dbname,
|
||||
|
||||
@@ -356,8 +356,11 @@ class DB {
|
||||
|
||||
// Removes the database entries in the range ["begin_key", "end_key"), i.e.,
|
||||
// including "begin_key" and excluding "end_key". Returns OK on success, and
|
||||
// a non-OK status on error. It is not an error if no keys exist in the range
|
||||
// ["begin_key", "end_key").
|
||||
// a non-OK status on error. It is not an error if the database does not
|
||||
// contain any existing data in the range ["begin_key", "end_key").
|
||||
//
|
||||
// If "end_key" comes before "start_key" according to the user's comparator,
|
||||
// a `Status::InvalidArgument` is returned.
|
||||
//
|
||||
// This feature is now usable in production, with the following caveats:
|
||||
// 1) Accumulating many range tombstones in the memtable will degrade read
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#pragma once
|
||||
|
||||
#define ROCKSDB_MAJOR 6
|
||||
#define ROCKSDB_MINOR 9
|
||||
#define ROCKSDB_MINOR 10
|
||||
#define ROCKSDB_PATCH 0
|
||||
|
||||
// Do not use these. We made the mistake of declaring macros starting with
|
||||
|
||||
+32
-10
@@ -20,6 +20,29 @@
|
||||
|
||||
namespace ROCKSDB_NAMESPACE {
|
||||
#ifndef ROCKSDB_LITE
|
||||
static std::unordered_map<std::string, WALRecoveryMode>
|
||||
wal_recovery_mode_string_map = {
|
||||
{"kTolerateCorruptedTailRecords",
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords},
|
||||
{"kAbsoluteConsistency", WALRecoveryMode::kAbsoluteConsistency},
|
||||
{"kPointInTimeRecovery", WALRecoveryMode::kPointInTimeRecovery},
|
||||
{"kSkipAnyCorruptedRecords",
|
||||
WALRecoveryMode::kSkipAnyCorruptedRecords}};
|
||||
|
||||
static std::unordered_map<std::string, DBOptions::AccessHint>
|
||||
access_hint_string_map = {{"NONE", DBOptions::AccessHint::NONE},
|
||||
{"NORMAL", DBOptions::AccessHint::NORMAL},
|
||||
{"SEQUENTIAL", DBOptions::AccessHint::SEQUENTIAL},
|
||||
{"WILLNEED", DBOptions::AccessHint::WILLNEED}};
|
||||
|
||||
static std::unordered_map<std::string, InfoLogLevel> info_log_level_string_map =
|
||||
{{"DEBUG_LEVEL", InfoLogLevel::DEBUG_LEVEL},
|
||||
{"INFO_LEVEL", InfoLogLevel::INFO_LEVEL},
|
||||
{"WARN_LEVEL", InfoLogLevel::WARN_LEVEL},
|
||||
{"ERROR_LEVEL", InfoLogLevel::ERROR_LEVEL},
|
||||
{"FATAL_LEVEL", InfoLogLevel::FATAL_LEVEL},
|
||||
{"HEADER_LEVEL", InfoLogLevel::HEADER_LEVEL}};
|
||||
|
||||
std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionsHelper::db_options_type_info = {
|
||||
/*
|
||||
@@ -247,10 +270,9 @@ std::unordered_map<std::string, OptionTypeInfo>
|
||||
{offsetof(struct DBOptions, allow_concurrent_memtable_write),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone, 0}},
|
||||
{"wal_recovery_mode",
|
||||
{offsetof(struct DBOptions, wal_recovery_mode),
|
||||
OptionType::kWALRecoveryMode, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone, 0}},
|
||||
{"wal_recovery_mode", OptionTypeInfo::Enum<WALRecoveryMode>(
|
||||
offsetof(struct DBOptions, wal_recovery_mode),
|
||||
&wal_recovery_mode_string_map)},
|
||||
{"enable_write_thread_adaptive_yield",
|
||||
{offsetof(struct DBOptions, enable_write_thread_adaptive_yield),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
@@ -268,12 +290,12 @@ std::unordered_map<std::string, OptionTypeInfo>
|
||||
OptionType::kUInt64T, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone, 0}},
|
||||
{"access_hint_on_compaction_start",
|
||||
{offsetof(struct DBOptions, access_hint_on_compaction_start),
|
||||
OptionType::kAccessHint, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone, 0}},
|
||||
{"info_log_level",
|
||||
{offsetof(struct DBOptions, info_log_level), OptionType::kInfoLogLevel,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone, 0}},
|
||||
OptionTypeInfo::Enum<DBOptions::AccessHint>(
|
||||
offsetof(struct DBOptions, access_hint_on_compaction_start),
|
||||
&access_hint_string_map)},
|
||||
{"info_log_level", OptionTypeInfo::Enum<InfoLogLevel>(
|
||||
offsetof(struct DBOptions, info_log_level),
|
||||
&info_log_level_string_map)},
|
||||
{"dump_malloc_stats",
|
||||
{offsetof(struct DBOptions, dump_malloc_stats), OptionType::kBoolean,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone, 0}},
|
||||
|
||||
@@ -273,29 +273,6 @@ std::vector<CompressionType> GetSupportedCompressions() {
|
||||
#ifndef ROCKSDB_LITE
|
||||
|
||||
namespace {
|
||||
template <typename T>
|
||||
bool ParseEnum(const std::unordered_map<std::string, T>& type_map,
|
||||
const std::string& type, T* value) {
|
||||
auto iter = type_map.find(type);
|
||||
if (iter != type_map.end()) {
|
||||
*value = iter->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool SerializeEnum(const std::unordered_map<std::string, T>& type_map,
|
||||
const T& type, std::string* value) {
|
||||
for (const auto& pair : type_map) {
|
||||
if (pair.second == type) {
|
||||
*value = pair.first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SerializeVectorCompressionType(const std::vector<CompressionType>& types,
|
||||
std::string* value) {
|
||||
std::stringstream ss;
|
||||
@@ -535,36 +512,10 @@ bool ParseOptionHelper(char* opt_address, const OptionType& opt_type,
|
||||
return ParseEnum<ChecksumType>(
|
||||
checksum_type_string_map, value,
|
||||
reinterpret_cast<ChecksumType*>(opt_address));
|
||||
case OptionType::kBlockBasedTableIndexType:
|
||||
return ParseEnum<BlockBasedTableOptions::IndexType>(
|
||||
block_base_table_index_type_string_map, value,
|
||||
reinterpret_cast<BlockBasedTableOptions::IndexType*>(opt_address));
|
||||
case OptionType::kBlockBasedTableDataBlockIndexType:
|
||||
return ParseEnum<BlockBasedTableOptions::DataBlockIndexType>(
|
||||
block_base_table_data_block_index_type_string_map, value,
|
||||
reinterpret_cast<BlockBasedTableOptions::DataBlockIndexType*>(
|
||||
opt_address));
|
||||
case OptionType::kBlockBasedTableIndexShorteningMode:
|
||||
return ParseEnum<BlockBasedTableOptions::IndexShorteningMode>(
|
||||
block_base_table_index_shortening_mode_string_map, value,
|
||||
reinterpret_cast<BlockBasedTableOptions::IndexShorteningMode*>(
|
||||
opt_address));
|
||||
case OptionType::kEncodingType:
|
||||
return ParseEnum<EncodingType>(
|
||||
encoding_type_string_map, value,
|
||||
reinterpret_cast<EncodingType*>(opt_address));
|
||||
case OptionType::kWALRecoveryMode:
|
||||
return ParseEnum<WALRecoveryMode>(
|
||||
wal_recovery_mode_string_map, value,
|
||||
reinterpret_cast<WALRecoveryMode*>(opt_address));
|
||||
case OptionType::kAccessHint:
|
||||
return ParseEnum<DBOptions::AccessHint>(
|
||||
access_hint_string_map, value,
|
||||
reinterpret_cast<DBOptions::AccessHint*>(opt_address));
|
||||
case OptionType::kInfoLogLevel:
|
||||
return ParseEnum<InfoLogLevel>(
|
||||
info_log_level_string_map, value,
|
||||
reinterpret_cast<InfoLogLevel*>(opt_address));
|
||||
case OptionType::kCompactionOptionsFIFO: {
|
||||
if (!FIFOCompactionOptionsSpecialCase(
|
||||
value, reinterpret_cast<CompactionOptionsFIFO*>(opt_address))) {
|
||||
@@ -729,24 +680,6 @@ bool SerializeSingleOptionHelper(const char* opt_address,
|
||||
return SerializeEnum<ChecksumType>(
|
||||
checksum_type_string_map,
|
||||
*reinterpret_cast<const ChecksumType*>(opt_address), value);
|
||||
case OptionType::kBlockBasedTableIndexType:
|
||||
return SerializeEnum<BlockBasedTableOptions::IndexType>(
|
||||
block_base_table_index_type_string_map,
|
||||
*reinterpret_cast<const BlockBasedTableOptions::IndexType*>(
|
||||
opt_address),
|
||||
value);
|
||||
case OptionType::kBlockBasedTableDataBlockIndexType:
|
||||
return SerializeEnum<BlockBasedTableOptions::DataBlockIndexType>(
|
||||
block_base_table_data_block_index_type_string_map,
|
||||
*reinterpret_cast<const BlockBasedTableOptions::DataBlockIndexType*>(
|
||||
opt_address),
|
||||
value);
|
||||
case OptionType::kBlockBasedTableIndexShorteningMode:
|
||||
return SerializeEnum<BlockBasedTableOptions::IndexShorteningMode>(
|
||||
block_base_table_index_shortening_mode_string_map,
|
||||
*reinterpret_cast<const BlockBasedTableOptions::IndexShorteningMode*>(
|
||||
opt_address),
|
||||
value);
|
||||
case OptionType::kFlushBlockPolicyFactory: {
|
||||
const auto* ptr =
|
||||
reinterpret_cast<const std::shared_ptr<FlushBlockPolicyFactory>*>(
|
||||
@@ -758,18 +691,6 @@ bool SerializeSingleOptionHelper(const char* opt_address,
|
||||
return SerializeEnum<EncodingType>(
|
||||
encoding_type_string_map,
|
||||
*reinterpret_cast<const EncodingType*>(opt_address), value);
|
||||
case OptionType::kWALRecoveryMode:
|
||||
return SerializeEnum<WALRecoveryMode>(
|
||||
wal_recovery_mode_string_map,
|
||||
*reinterpret_cast<const WALRecoveryMode*>(opt_address), value);
|
||||
case OptionType::kAccessHint:
|
||||
return SerializeEnum<DBOptions::AccessHint>(
|
||||
access_hint_string_map,
|
||||
*reinterpret_cast<const DBOptions::AccessHint*>(opt_address), value);
|
||||
case OptionType::kInfoLogLevel:
|
||||
return SerializeEnum<InfoLogLevel>(
|
||||
info_log_level_string_map,
|
||||
*reinterpret_cast<const InfoLogLevel*>(opt_address), value);
|
||||
case OptionType::kCompactionOptionsFIFO:
|
||||
return SerializeStruct(opt_address, value,
|
||||
fifo_compaction_options_type_info);
|
||||
@@ -1231,32 +1152,6 @@ Status GetTableFactoryFromMap(
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, BlockBasedTableOptions::IndexType>
|
||||
OptionsHelper::block_base_table_index_type_string_map = {
|
||||
{"kBinarySearch", BlockBasedTableOptions::IndexType::kBinarySearch},
|
||||
{"kHashSearch", BlockBasedTableOptions::IndexType::kHashSearch},
|
||||
{"kTwoLevelIndexSearch",
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch},
|
||||
{"kBinarySearchWithFirstKey",
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey}};
|
||||
|
||||
std::unordered_map<std::string, BlockBasedTableOptions::DataBlockIndexType>
|
||||
OptionsHelper::block_base_table_data_block_index_type_string_map = {
|
||||
{"kDataBlockBinarySearch",
|
||||
BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch},
|
||||
{"kDataBlockBinaryAndHash",
|
||||
BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinaryAndHash}};
|
||||
|
||||
std::unordered_map<std::string, BlockBasedTableOptions::IndexShorteningMode>
|
||||
OptionsHelper::block_base_table_index_shortening_mode_string_map = {
|
||||
{"kNoShortening",
|
||||
BlockBasedTableOptions::IndexShorteningMode::kNoShortening},
|
||||
{"kShortenSeparators",
|
||||
BlockBasedTableOptions::IndexShorteningMode::kShortenSeparators},
|
||||
{"kShortenSeparatorsAndSuccessor",
|
||||
BlockBasedTableOptions::IndexShorteningMode::
|
||||
kShortenSeparatorsAndSuccessor}};
|
||||
|
||||
std::unordered_map<std::string, EncodingType>
|
||||
OptionsHelper::encoding_type_string_map = {{"kPlain", kPlain},
|
||||
{"kPrefix", kPrefix}};
|
||||
@@ -1275,31 +1170,6 @@ std::unordered_map<std::string, CompactionPri>
|
||||
{"kOldestSmallestSeqFirst", kOldestSmallestSeqFirst},
|
||||
{"kMinOverlappingRatio", kMinOverlappingRatio}};
|
||||
|
||||
std::unordered_map<std::string, WALRecoveryMode>
|
||||
OptionsHelper::wal_recovery_mode_string_map = {
|
||||
{"kTolerateCorruptedTailRecords",
|
||||
WALRecoveryMode::kTolerateCorruptedTailRecords},
|
||||
{"kAbsoluteConsistency", WALRecoveryMode::kAbsoluteConsistency},
|
||||
{"kPointInTimeRecovery", WALRecoveryMode::kPointInTimeRecovery},
|
||||
{"kSkipAnyCorruptedRecords",
|
||||
WALRecoveryMode::kSkipAnyCorruptedRecords}};
|
||||
|
||||
std::unordered_map<std::string, DBOptions::AccessHint>
|
||||
OptionsHelper::access_hint_string_map = {
|
||||
{"NONE", DBOptions::AccessHint::NONE},
|
||||
{"NORMAL", DBOptions::AccessHint::NORMAL},
|
||||
{"SEQUENTIAL", DBOptions::AccessHint::SEQUENTIAL},
|
||||
{"WILLNEED", DBOptions::AccessHint::WILLNEED}};
|
||||
|
||||
std::unordered_map<std::string, InfoLogLevel>
|
||||
OptionsHelper::info_log_level_string_map = {
|
||||
{"DEBUG_LEVEL", InfoLogLevel::DEBUG_LEVEL},
|
||||
{"INFO_LEVEL", InfoLogLevel::INFO_LEVEL},
|
||||
{"WARN_LEVEL", InfoLogLevel::WARN_LEVEL},
|
||||
{"ERROR_LEVEL", InfoLogLevel::ERROR_LEVEL},
|
||||
{"FATAL_LEVEL", InfoLogLevel::FATAL_LEVEL},
|
||||
{"HEADER_LEVEL", InfoLogLevel::HEADER_LEVEL}};
|
||||
|
||||
LRUCacheOptions OptionsHelper::dummy_lru_cache_options;
|
||||
CompactionOptionsUniversal OptionsHelper::dummy_comp_options_universal;
|
||||
CompactionOptionsFIFO OptionsHelper::dummy_comp_options;
|
||||
@@ -1510,21 +1380,6 @@ static bool AreOptionsEqual(OptionType type, const char* this_offset,
|
||||
return IsOptionEqual<ChecksumType>(this_offset, that_offset);
|
||||
case OptionType::kEncodingType:
|
||||
return IsOptionEqual<EncodingType>(this_offset, that_offset);
|
||||
case OptionType::kBlockBasedTableIndexType:
|
||||
return IsOptionEqual<BlockBasedTableOptions::IndexType>(this_offset,
|
||||
that_offset);
|
||||
case OptionType::kBlockBasedTableDataBlockIndexType:
|
||||
return IsOptionEqual<BlockBasedTableOptions::DataBlockIndexType>(
|
||||
this_offset, that_offset);
|
||||
case OptionType::kBlockBasedTableIndexShorteningMode:
|
||||
return IsOptionEqual<BlockBasedTableOptions::IndexShorteningMode>(
|
||||
this_offset, that_offset);
|
||||
case OptionType::kWALRecoveryMode:
|
||||
return IsOptionEqual<WALRecoveryMode>(this_offset, that_offset);
|
||||
case OptionType::kAccessHint:
|
||||
return IsOptionEqual<DBOptions::AccessHint>(this_offset, that_offset);
|
||||
case OptionType::kInfoLogLevel:
|
||||
return IsOptionEqual<InfoLogLevel>(this_offset, that_offset);
|
||||
case OptionType::kCompactionOptionsFIFO: {
|
||||
CompactionOptionsFIFO lhs =
|
||||
*reinterpret_cast<const CompactionOptionsFIFO*>(this_offset);
|
||||
|
||||
@@ -107,25 +107,11 @@ struct OptionsHelper {
|
||||
static std::unordered_map<std::string, OptionTypeInfo> db_options_type_info;
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
lru_cache_options_type_info;
|
||||
static std::unordered_map<std::string, BlockBasedTableOptions::IndexType>
|
||||
block_base_table_index_type_string_map;
|
||||
static std::unordered_map<std::string,
|
||||
BlockBasedTableOptions::DataBlockIndexType>
|
||||
block_base_table_data_block_index_type_string_map;
|
||||
static std::unordered_map<std::string,
|
||||
BlockBasedTableOptions::IndexShorteningMode>
|
||||
block_base_table_index_shortening_mode_string_map;
|
||||
static std::unordered_map<std::string, EncodingType> encoding_type_string_map;
|
||||
static std::unordered_map<std::string, CompactionStyle>
|
||||
compaction_style_string_map;
|
||||
static std::unordered_map<std::string, CompactionPri>
|
||||
compaction_pri_string_map;
|
||||
static std::unordered_map<std::string, WALRecoveryMode>
|
||||
wal_recovery_mode_string_map;
|
||||
static std::unordered_map<std::string, DBOptions::AccessHint>
|
||||
access_hint_string_map;
|
||||
static std::unordered_map<std::string, InfoLogLevel>
|
||||
info_log_level_string_map;
|
||||
static ColumnFamilyOptions dummy_cf_options;
|
||||
static CompactionOptionsFIFO dummy_comp_options;
|
||||
static LRUCacheOptions dummy_lru_cache_options;
|
||||
@@ -153,22 +139,11 @@ static auto& lru_cache_options_type_info =
|
||||
OptionsHelper::lru_cache_options_type_info;
|
||||
static auto& compression_type_string_map =
|
||||
OptionsHelper::compression_type_string_map;
|
||||
static auto& block_base_table_index_type_string_map =
|
||||
OptionsHelper::block_base_table_index_type_string_map;
|
||||
static auto& block_base_table_data_block_index_type_string_map =
|
||||
OptionsHelper::block_base_table_data_block_index_type_string_map;
|
||||
static auto& block_base_table_index_shortening_mode_string_map =
|
||||
OptionsHelper::block_base_table_index_shortening_mode_string_map;
|
||||
static auto& encoding_type_string_map = OptionsHelper::encoding_type_string_map;
|
||||
static auto& compaction_style_string_map =
|
||||
OptionsHelper::compaction_style_string_map;
|
||||
static auto& compaction_pri_string_map =
|
||||
OptionsHelper::compaction_pri_string_map;
|
||||
static auto& wal_recovery_mode_string_map =
|
||||
OptionsHelper::wal_recovery_mode_string_map;
|
||||
static auto& access_hint_string_map = OptionsHelper::access_hint_string_map;
|
||||
static auto& info_log_level_string_map =
|
||||
OptionsHelper::info_log_level_string_map;
|
||||
#endif // !ROCKSDB_LITE
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
@@ -3004,6 +3004,17 @@ static void TestAndCompareOption(const ConfigOptions& config_options,
|
||||
comp_addr, &mismatch));
|
||||
}
|
||||
|
||||
static void TestAndCompareOption(const ConfigOptions& config_options,
|
||||
const OptionTypeInfo& opt_info,
|
||||
const std::string& opt_name,
|
||||
const std::string& opt_value, void* base_ptr,
|
||||
void* comp_ptr) {
|
||||
char* base_addr = reinterpret_cast<char*>(base_ptr) + opt_info.offset;
|
||||
ASSERT_OK(
|
||||
opt_info.ParseOption(config_options, opt_name, opt_value, base_addr));
|
||||
TestAndCompareOption(config_options, opt_info, opt_name, base_ptr, comp_ptr);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void TestOptInfo(const ConfigOptions& config_options, OptionType opt_type,
|
||||
T* base, T* comp) {
|
||||
@@ -3264,6 +3275,87 @@ TEST_F(OptionTypeInfoTest, TestOptionFlags) {
|
||||
ASSERT_NE(base, comp);
|
||||
}
|
||||
|
||||
TEST_F(OptionTypeInfoTest, TestCustomEnum) {
|
||||
enum TestEnum { kA, kB, kC };
|
||||
std::unordered_map<std::string, TestEnum> enum_map = {
|
||||
{"A", TestEnum::kA},
|
||||
{"B", TestEnum::kB},
|
||||
{"C", TestEnum::kC},
|
||||
};
|
||||
OptionTypeInfo opt_info = OptionTypeInfo::Enum<TestEnum>(0, &enum_map);
|
||||
TestEnum e1, e2;
|
||||
ConfigOptions config_options;
|
||||
std::string result, mismatch;
|
||||
|
||||
e2 = TestEnum::kA;
|
||||
|
||||
ASSERT_OK(opt_info.ParseOption(config_options, "", "B",
|
||||
reinterpret_cast<char*>(&e1)));
|
||||
ASSERT_OK(opt_info.SerializeOption(config_options, "",
|
||||
reinterpret_cast<char*>(&e1), &result));
|
||||
ASSERT_EQ(e1, TestEnum::kB);
|
||||
ASSERT_EQ(result, "B");
|
||||
|
||||
ASSERT_FALSE(opt_info.MatchesOption(config_options, "Enum",
|
||||
reinterpret_cast<char*>(&e1),
|
||||
reinterpret_cast<char*>(&e2), &mismatch));
|
||||
ASSERT_EQ(mismatch, "Enum");
|
||||
|
||||
TestAndCompareOption(config_options, opt_info, "", "C",
|
||||
reinterpret_cast<char*>(&e1),
|
||||
reinterpret_cast<char*>(&e2));
|
||||
ASSERT_EQ(e2, TestEnum::kC);
|
||||
|
||||
ASSERT_NOK(opt_info.ParseOption(config_options, "", "D",
|
||||
reinterpret_cast<char*>(&e1)));
|
||||
ASSERT_EQ(e1, TestEnum::kC);
|
||||
}
|
||||
|
||||
TEST_F(OptionTypeInfoTest, TestBuiltinEnum) {
|
||||
ConfigOptions config_options;
|
||||
for (auto iter : OptionsHelper::compaction_style_string_map) {
|
||||
CompactionStyle e1, e2;
|
||||
TestAndCompareOption(config_options,
|
||||
OptionTypeInfo(0, OptionType::kCompactionStyle),
|
||||
"CompactionStyle", iter.first, &e1, &e2);
|
||||
ASSERT_EQ(e1, iter.second);
|
||||
}
|
||||
for (auto iter : OptionsHelper::compaction_pri_string_map) {
|
||||
CompactionPri e1, e2;
|
||||
TestAndCompareOption(config_options,
|
||||
OptionTypeInfo(0, OptionType::kCompactionPri),
|
||||
"CompactionPri", iter.first, &e1, &e2);
|
||||
ASSERT_EQ(e1, iter.second);
|
||||
}
|
||||
for (auto iter : OptionsHelper::compression_type_string_map) {
|
||||
CompressionType e1, e2;
|
||||
TestAndCompareOption(config_options,
|
||||
OptionTypeInfo(0, OptionType::kCompressionType),
|
||||
"CompressionType", iter.first, &e1, &e2);
|
||||
ASSERT_EQ(e1, iter.second);
|
||||
}
|
||||
for (auto iter : OptionsHelper::compaction_stop_style_string_map) {
|
||||
CompactionStopStyle e1, e2;
|
||||
TestAndCompareOption(config_options,
|
||||
OptionTypeInfo(0, OptionType::kCompactionStopStyle),
|
||||
"CompactionStopStyle", iter.first, &e1, &e2);
|
||||
ASSERT_EQ(e1, iter.second);
|
||||
}
|
||||
for (auto iter : OptionsHelper::checksum_type_string_map) {
|
||||
ChecksumType e1, e2;
|
||||
TestAndCompareOption(config_options,
|
||||
OptionTypeInfo(0, OptionType::kChecksumType),
|
||||
"CheckSumType", iter.first, &e1, &e2);
|
||||
ASSERT_EQ(e1, iter.second);
|
||||
}
|
||||
for (auto iter : OptionsHelper::encoding_type_string_map) {
|
||||
EncodingType e1, e2;
|
||||
TestAndCompareOption(config_options,
|
||||
OptionTypeInfo(0, OptionType::kEncodingType),
|
||||
"EncodingType", iter.first, &e1, &e2);
|
||||
ASSERT_EQ(e1, iter.second);
|
||||
}
|
||||
}
|
||||
#endif // !ROCKSDB_LITE
|
||||
} // namespace ROCKSDB_NAMESPACE
|
||||
|
||||
|
||||
+91
-6
@@ -41,18 +41,13 @@ enum class OptionType {
|
||||
kCompactionStopStyle,
|
||||
kMergeOperator,
|
||||
kMemTableRepFactory,
|
||||
kBlockBasedTableIndexType,
|
||||
kBlockBasedTableDataBlockIndexType,
|
||||
kBlockBasedTableIndexShorteningMode,
|
||||
kFilterPolicy,
|
||||
kFlushBlockPolicyFactory,
|
||||
kChecksumType,
|
||||
kEncodingType,
|
||||
kWALRecoveryMode,
|
||||
kAccessHint,
|
||||
kInfoLogLevel,
|
||||
kLRUCacheOptions,
|
||||
kEnv,
|
||||
kEnum,
|
||||
kUnknown,
|
||||
};
|
||||
|
||||
@@ -97,6 +92,39 @@ inline OptionTypeFlags operator&(const OptionTypeFlags &a,
|
||||
static_cast<uint32_t>(b));
|
||||
}
|
||||
|
||||
// Converts an string into its enumerated value.
|
||||
// @param type_map Mapping between strings and enum values
|
||||
// @param type The string representation of the enum
|
||||
// @param value Returns the enum value represented by the string
|
||||
// @return true if the string was found in the enum map, false otherwise.
|
||||
template <typename T>
|
||||
bool ParseEnum(const std::unordered_map<std::string, T>& type_map,
|
||||
const std::string& type, T* value) {
|
||||
auto iter = type_map.find(type);
|
||||
if (iter != type_map.end()) {
|
||||
*value = iter->second;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Converts an enum into its string representation.
|
||||
// @param type_map Mapping between strings and enum values
|
||||
// @param type The enum
|
||||
// @param value Returned as the string representation of the enum
|
||||
// @return true if the enum was found in the enum map, false otherwise.
|
||||
template <typename T>
|
||||
bool SerializeEnum(const std::unordered_map<std::string, T>& type_map,
|
||||
const T& type, std::string* value) {
|
||||
for (const auto& pair : type_map) {
|
||||
if (pair.second == type) {
|
||||
*value = pair.first;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Function for converting a option string value into its underlying
|
||||
// representation in "addr"
|
||||
// On success, Status::OK is returned and addr is set to the parsed form
|
||||
@@ -198,6 +226,63 @@ class OptionTypeInfo {
|
||||
verification(_verification),
|
||||
flags(_flags) {}
|
||||
|
||||
// Creates an OptionTypeInfo for an enum type. Enums use an additional
|
||||
// map to convert the enums to/from their string representation.
|
||||
// To create an OptionTypeInfo that is an Enum, one should:
|
||||
// - Create a static map of string values to the corresponding enum value
|
||||
// - Call this method passing the static map in as a parameter.
|
||||
// Note that it is not necessary to add a new OptionType or make any
|
||||
// other changes -- the returned object handles parsing, serialiation, and
|
||||
// comparisons.
|
||||
//
|
||||
// @param _offset The offset in the option object for this enum
|
||||
// @param map The string to enum mapping for this enum
|
||||
template <typename T>
|
||||
static OptionTypeInfo Enum(
|
||||
int _offset, const std::unordered_map<std::string, T>* const map) {
|
||||
return OptionTypeInfo(
|
||||
_offset, OptionType::kEnum, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone, 0,
|
||||
// Uses the map argument to convert the input string into
|
||||
// its corresponding enum value. If value is found in the map,
|
||||
// addr is updated to the corresponding map entry.
|
||||
// @return OK if the value is found in the map
|
||||
// @return InvalidArgument if the value is not found in the map
|
||||
[map](const ConfigOptions&, const std::string& name,
|
||||
const std::string& value, char* addr) {
|
||||
if (map == nullptr) {
|
||||
return Status::NotSupported("No enum mapping ", name);
|
||||
} else if (ParseEnum<T>(*map, value, reinterpret_cast<T*>(addr))) {
|
||||
return Status::OK();
|
||||
} else {
|
||||
return Status::InvalidArgument("No mapping for enum ", name);
|
||||
}
|
||||
},
|
||||
// Uses the map argument to convert the input enum into
|
||||
// its corresponding string value. If enum value is found in the map,
|
||||
// value is updated to the corresponding string value in the map.
|
||||
// @return OK if the enum is found in the map
|
||||
// @return InvalidArgument if the enum is not found in the map
|
||||
[map](const ConfigOptions&, const std::string& name, const char* addr,
|
||||
std::string* value) {
|
||||
if (map == nullptr) {
|
||||
return Status::NotSupported("No enum mapping ", name);
|
||||
} else if (SerializeEnum<T>(*map, (*reinterpret_cast<const T*>(addr)),
|
||||
value)) {
|
||||
return Status::OK();
|
||||
} else {
|
||||
return Status::InvalidArgument("No mapping for enum ", name);
|
||||
}
|
||||
},
|
||||
// Casts addr1 and addr2 to the enum type and returns true if
|
||||
// they are equal, false otherwise.
|
||||
[](const ConfigOptions&, const std::string&, const char* addr1,
|
||||
const char* addr2, std::string*) {
|
||||
return (*reinterpret_cast<const T*>(addr1) ==
|
||||
*reinterpret_cast<const T*>(addr2));
|
||||
});
|
||||
} // End OptionTypeInfo::Enum
|
||||
|
||||
bool IsEnabled(OptionTypeFlags otf) const { return (flags & otf) == otf; }
|
||||
|
||||
bool IsMutable() const { return IsEnabled(OptionTypeFlags::kMutable); }
|
||||
|
||||
@@ -160,6 +160,34 @@ size_t TailPrefetchStats::GetSuggestedPrefetchSize() {
|
||||
}
|
||||
|
||||
#ifndef ROCKSDB_LITE
|
||||
static std::unordered_map<std::string, BlockBasedTableOptions::IndexType>
|
||||
block_base_table_index_type_string_map = {
|
||||
{"kBinarySearch", BlockBasedTableOptions::IndexType::kBinarySearch},
|
||||
{"kHashSearch", BlockBasedTableOptions::IndexType::kHashSearch},
|
||||
{"kTwoLevelIndexSearch",
|
||||
BlockBasedTableOptions::IndexType::kTwoLevelIndexSearch},
|
||||
{"kBinarySearchWithFirstKey",
|
||||
BlockBasedTableOptions::IndexType::kBinarySearchWithFirstKey}};
|
||||
|
||||
static std::unordered_map<std::string,
|
||||
BlockBasedTableOptions::DataBlockIndexType>
|
||||
block_base_table_data_block_index_type_string_map = {
|
||||
{"kDataBlockBinarySearch",
|
||||
BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinarySearch},
|
||||
{"kDataBlockBinaryAndHash",
|
||||
BlockBasedTableOptions::DataBlockIndexType::kDataBlockBinaryAndHash}};
|
||||
|
||||
static std::unordered_map<std::string,
|
||||
BlockBasedTableOptions::IndexShorteningMode>
|
||||
block_base_table_index_shortening_mode_string_map = {
|
||||
{"kNoShortening",
|
||||
BlockBasedTableOptions::IndexShorteningMode::kNoShortening},
|
||||
{"kShortenSeparators",
|
||||
BlockBasedTableOptions::IndexShorteningMode::kShortenSeparators},
|
||||
{"kShortenSeparatorsAndSuccessor",
|
||||
BlockBasedTableOptions::IndexShorteningMode::
|
||||
kShortenSeparatorsAndSuccessor}};
|
||||
|
||||
static std::unordered_map<std::string, OptionTypeInfo>
|
||||
block_based_table_type_info = {
|
||||
/* currently not supported
|
||||
@@ -185,22 +213,21 @@ static std::unordered_map<std::string, OptionTypeInfo>
|
||||
pin_l0_filter_and_index_blocks_in_cache),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone, 0}},
|
||||
{"index_type",
|
||||
{offsetof(struct BlockBasedTableOptions, index_type),
|
||||
OptionType::kBlockBasedTableIndexType,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone, 0}},
|
||||
{"index_type", OptionTypeInfo::Enum<BlockBasedTableOptions::IndexType>(
|
||||
offsetof(struct BlockBasedTableOptions, index_type),
|
||||
&block_base_table_index_type_string_map)},
|
||||
{"hash_index_allow_collision",
|
||||
{offsetof(struct BlockBasedTableOptions, hash_index_allow_collision),
|
||||
OptionType::kBoolean, OptionVerificationType::kNormal,
|
||||
OptionTypeFlags::kNone, 0}},
|
||||
{"data_block_index_type",
|
||||
{offsetof(struct BlockBasedTableOptions, data_block_index_type),
|
||||
OptionType::kBlockBasedTableDataBlockIndexType,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone, 0}},
|
||||
OptionTypeInfo::Enum<BlockBasedTableOptions::DataBlockIndexType>(
|
||||
offsetof(struct BlockBasedTableOptions, data_block_index_type),
|
||||
&block_base_table_data_block_index_type_string_map)},
|
||||
{"index_shortening",
|
||||
{offsetof(struct BlockBasedTableOptions, index_shortening),
|
||||
OptionType::kBlockBasedTableIndexShorteningMode,
|
||||
OptionVerificationType::kNormal, OptionTypeFlags::kNone, 0}},
|
||||
OptionTypeInfo::Enum<BlockBasedTableOptions::IndexShorteningMode>(
|
||||
offsetof(struct BlockBasedTableOptions, index_shortening),
|
||||
&block_base_table_index_shortening_mode_string_map)},
|
||||
{"data_block_hash_table_util_ratio",
|
||||
{offsetof(struct BlockBasedTableOptions,
|
||||
data_block_hash_table_util_ratio),
|
||||
|
||||
+12
-4
@@ -5415,13 +5415,15 @@ class Benchmark {
|
||||
|
||||
// Select one key in the key-range and compose the keyID
|
||||
int64_t key_offset = 0, key_seed;
|
||||
if (key_dist_a == 0.0 && key_dist_b == 0.0) {
|
||||
if (key_dist_a == 0.0 || key_dist_b == 0.0) {
|
||||
key_offset = ini_rand % keyrange_size_;
|
||||
} else {
|
||||
double u =
|
||||
static_cast<double>(ini_rand % keyrange_size_) / keyrange_size_;
|
||||
key_seed = static_cast<int64_t>(
|
||||
ceil(std::pow((ini_rand / key_dist_a), (1 / key_dist_b))));
|
||||
ceil(std::pow((u / key_dist_a), (1 / key_dist_b))));
|
||||
Random64 rand_key(key_seed);
|
||||
key_offset = static_cast<int64_t>(rand_key.Next()) % keyrange_size_;
|
||||
key_offset = rand_key.Next() % keyrange_size_;
|
||||
}
|
||||
return keyrange_size_ * keyrange_id + key_offset;
|
||||
}
|
||||
@@ -5448,6 +5450,7 @@ class Benchmark {
|
||||
double write_rate = 1000000.0;
|
||||
double read_rate = 1000000.0;
|
||||
bool use_prefix_modeling = false;
|
||||
bool use_random_modeling = false;
|
||||
GenerateTwoTermExpKeys gen_exp;
|
||||
std::vector<double> ratio{FLAGS_mix_get_ratio, FLAGS_mix_put_ratio,
|
||||
FLAGS_mix_seek_ratio};
|
||||
@@ -5482,6 +5485,9 @@ class Benchmark {
|
||||
FLAGS_num, FLAGS_keyrange_dist_a, FLAGS_keyrange_dist_b,
|
||||
FLAGS_keyrange_dist_c, FLAGS_keyrange_dist_d);
|
||||
}
|
||||
if (FLAGS_key_dist_a == 0 || FLAGS_key_dist_b == 0) {
|
||||
use_random_modeling = true;
|
||||
}
|
||||
|
||||
Duration duration(FLAGS_duration, reads_);
|
||||
while (!duration.Done(1)) {
|
||||
@@ -5492,7 +5498,9 @@ class Benchmark {
|
||||
double u = static_cast<double>(rand_v) / FLAGS_num;
|
||||
|
||||
// Generate the keyID based on the key hotness and prefix hotness
|
||||
if (use_prefix_modeling) {
|
||||
if (use_random_modeling) {
|
||||
key_rand = ini_rand;
|
||||
} else if (use_prefix_modeling) {
|
||||
key_rand =
|
||||
gen_exp.DistGetKeyID(ini_rand, FLAGS_key_dist_a, FLAGS_key_dist_b);
|
||||
} else {
|
||||
|
||||
@@ -422,6 +422,12 @@ def whitebox_crash_main(args, unknown_args):
|
||||
"ops_per_thread": cmd_params['ops_per_thread'],
|
||||
"compaction_style": 1,
|
||||
}
|
||||
# Single level universal has a lot of special logic. Ensure we cover
|
||||
# it sometimes.
|
||||
if random.randint(0, 1) == 1:
|
||||
additional_opts.update({
|
||||
"num_levels": 1,
|
||||
})
|
||||
elif check_mode == 2:
|
||||
# normal run with FIFO compaction mode
|
||||
# ops_per_thread is divided by 5 because FIFO compaction
|
||||
|
||||
@@ -25,8 +25,9 @@ namespace blob_db {
|
||||
// users to use blob DB.
|
||||
|
||||
struct BlobDBOptions {
|
||||
// name of the directory under main db, where blobs will be stored.
|
||||
// default is "blob_dir"
|
||||
// Name of the directory under the base DB where blobs will be stored. Using
|
||||
// a directory where the base DB stores its SST files is not supported.
|
||||
// Default is "blob_dir"
|
||||
std::string blob_dir = "blob_dir";
|
||||
|
||||
// whether the blob_dir path is relative or absolute.
|
||||
|
||||
@@ -211,6 +211,34 @@ Status BlobDBImpl::Open(std::vector<ColumnFamilyHandle*>* handles) {
|
||||
}
|
||||
db_impl_ = static_cast_with_check<DBImpl>(db_->GetRootDB());
|
||||
|
||||
// Sanitize the blob_dir provided. Using a directory where the
|
||||
// base DB stores its files for the default CF is not supported.
|
||||
const ColumnFamilyData* const cfd =
|
||||
static_cast<ColumnFamilyHandleImpl*>(DefaultColumnFamily())->cfd();
|
||||
assert(cfd);
|
||||
|
||||
const ImmutableCFOptions* const ioptions = cfd->ioptions();
|
||||
assert(ioptions);
|
||||
|
||||
assert(env_);
|
||||
|
||||
for (const auto& cf_path : ioptions->cf_paths) {
|
||||
bool blob_dir_same_as_cf_dir = false;
|
||||
s = env_->AreFilesSame(blob_dir_, cf_path.path, &blob_dir_same_as_cf_dir);
|
||||
if (!s.ok()) {
|
||||
ROCKS_LOG_ERROR(db_options_.info_log,
|
||||
"Error while sanitizing blob_dir %s, status: %s",
|
||||
blob_dir_.c_str(), s.ToString().c_str());
|
||||
return s;
|
||||
}
|
||||
|
||||
if (blob_dir_same_as_cf_dir) {
|
||||
return Status::NotSupported(
|
||||
"Using the base DB's storage directories for BlobDB files is not "
|
||||
"supported.");
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize SST file <-> oldest blob file mapping if garbage collection
|
||||
// is enabled.
|
||||
if (bdb_options_.enable_garbage_collection) {
|
||||
|
||||
Reference in new issue
Block a user