Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 78e291b

Browse files
ltamasifacebook-github-bot
authored andcommitted
Improve consistency checks in VersionBuilder (#6901)
Summary: The patch cleans up the code and improves the consistency checks around adding/deleting table files in `VersionBuilder`. Namely, it makes the checks stricter and improves them in the following ways: 1) A table file can now only be deleted from the LSM tree using the level it resides on. Earlier, there was some unnecessary wiggle room for trivially moved files (they could be deleted using a lower level number than the actual one). 2) A table file cannot be added to the tree if it is already present in the tree on any level (not just the target level). The earlier code only had an assertion (which is a no-op in release builds) that the newly added file is not already present on the target level. 3) The above consistency checks around state transitions are now mandatory, as opposed to the earlier `CheckConsistencyForDeletes`, which was a no-op in release mode unless `force_consistency_checks` was set to `true`. The rationale here is that assuming that the initial state is consistent, a valid transition leads to a next state that is also consistent; however, an *invalid* transition offers no such guarantee. Hence it makes sense to validate the transitions unconditionally, and save `force_consistency_checks` for the paranoid checks that re-validate the entire state. 4) The new checks build on the mechanism introduced in #6862, which enables us to efficiently look up the location (level and position within level) of files in a `Version` by file number. This makes the consistency checks much more efficient than the earlier `CheckConsistencyForDeletes`, which essentially performed a linear search. Pull Request resolved: #6901 Test Plan: Extended the unit tests and ran: `make check` `make whitebox_crash_test` Reviewed By: ajkr Differential Revision: D21822714 Pulled By: ltamasi fbshipit-source-id: e2b29c8b6da1bf0f59004acc889e4870b2d18215
1 parent 9360776 commit 78e291b

2 files changed

Lines changed: 367 additions & 98 deletions

File tree

db/version_builder.cc

Lines changed: 137 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,16 @@ class VersionBuilder::Rep {
130130
VersionSet* version_set_;
131131
int num_levels_;
132132
LevelState* levels_;
133-
// Store states of levels larger than num_levels_. We do this instead of
133+
// Store sizes of levels larger than num_levels_. We do this instead of
134134
// storing them in levels_ to avoid regression in case there are no files
135135
// on invalid levels. The version is not consistent if in the end the files
136136
// on invalid levels don't cancel out.
137-
std::map<int, std::unordered_set<uint64_t>> invalid_levels_;
137+
std::unordered_map<int, size_t> invalid_level_sizes_;
138138
// Whether there are invalid new files or invalid deletion on levels larger
139139
// than num_levels_.
140140
bool has_invalid_levels_;
141+
// Current levels of table files affected by additions/deletions.
142+
std::unordered_map<uint64_t, int> table_file_levels_;
141143
FileComparator level_zero_cmp_;
142144
FileComparator level_nonzero_cmp_;
143145

@@ -360,65 +362,19 @@ class VersionBuilder::Rep {
360362
return ret_s;
361363
}
362364

363-
Status CheckConsistencyForDeletes(VersionEdit* /*edit*/, uint64_t number,
364-
int level) {
365-
#ifdef NDEBUG
366-
if (!base_vstorage_->force_consistency_checks()) {
367-
// Dont run consistency checks in release mode except if
368-
// explicitly asked to
369-
return Status::OK();
370-
}
371-
#endif
372-
// a file to be deleted better exist in the previous version
373-
bool found = false;
374-
for (int l = 0; !found && l < num_levels_; l++) {
375-
const std::vector<FileMetaData*>& base_files =
376-
base_vstorage_->LevelFiles(l);
377-
for (size_t i = 0; i < base_files.size(); i++) {
378-
FileMetaData* f = base_files[i];
379-
if (f->fd.GetNumber() == number) {
380-
found = true;
381-
break;
382-
}
383-
}
384-
}
385-
// if the file did not exist in the previous version, then it
386-
// is possibly moved from lower level to higher level in current
387-
// version
388-
for (int l = level + 1; !found && l < num_levels_; l++) {
389-
auto& level_added = levels_[l].added_files;
390-
auto got = level_added.find(number);
391-
if (got != level_added.end()) {
392-
found = true;
393-
break;
394-
}
395-
}
396-
397-
// maybe this file was added in a previous edit that was Applied
398-
if (!found) {
399-
auto& level_added = levels_[level].added_files;
400-
auto got = level_added.find(number);
401-
if (got != level_added.end()) {
402-
found = true;
403-
}
404-
}
405-
if (!found) {
406-
fprintf(stderr, "not found %" PRIu64 "\n", number);
407-
return Status::Corruption("not found " + NumberToString(number));
408-
}
409-
return Status::OK();
410-
}
411-
412-
bool CheckConsistencyForNumLevels() {
365+
bool CheckConsistencyForNumLevels() const {
413366
// Make sure there are no files on or beyond num_levels().
414367
if (has_invalid_levels_) {
415368
return false;
416369
}
417-
for (auto& level : invalid_levels_) {
418-
if (level.second.size() > 0) {
370+
371+
for (const auto& pair : invalid_level_sizes_) {
372+
const size_t level_size = pair.second;
373+
if (level_size != 0) {
419374
return false;
420375
}
421376
}
377+
422378
return true;
423379
}
424380

@@ -479,78 +435,162 @@ class VersionBuilder::Rep {
479435
return Status::OK();
480436
}
481437

438+
int GetCurrentLevelForTableFile(uint64_t file_number) const {
439+
auto it = table_file_levels_.find(file_number);
440+
if (it != table_file_levels_.end()) {
441+
return it->second;
442+
}
443+
444+
assert(base_vstorage_);
445+
return base_vstorage_->GetFileLocation(file_number).GetLevel();
446+
}
447+
448+
Status ApplyFileDeletion(int level, uint64_t file_number) {
449+
assert(level != VersionStorageInfo::FileLocation::Invalid().GetLevel());
450+
451+
const int current_level = GetCurrentLevelForTableFile(file_number);
452+
453+
if (level != current_level) {
454+
if (level >= num_levels_) {
455+
has_invalid_levels_ = true;
456+
}
457+
458+
std::ostringstream oss;
459+
oss << "Cannot delete table file #" << file_number << " from level "
460+
<< level << " since it is ";
461+
if (current_level ==
462+
VersionStorageInfo::FileLocation::Invalid().GetLevel()) {
463+
oss << "not in the LSM tree";
464+
} else {
465+
oss << "on level " << current_level;
466+
}
467+
468+
return Status::Corruption("VersionBuilder", oss.str());
469+
}
470+
471+
if (level >= num_levels_) {
472+
assert(invalid_level_sizes_[level] > 0);
473+
--invalid_level_sizes_[level];
474+
475+
table_file_levels_[file_number] =
476+
VersionStorageInfo::FileLocation::Invalid().GetLevel();
477+
478+
return Status::OK();
479+
}
480+
481+
auto& level_state = levels_[level];
482+
483+
auto& add_files = level_state.added_files;
484+
auto add_it = add_files.find(file_number);
485+
if (add_it != add_files.end()) {
486+
UnrefFile(add_it->second);
487+
add_files.erase(add_it);
488+
} else {
489+
auto& del_files = level_state.deleted_files;
490+
assert(del_files.find(file_number) == del_files.end());
491+
del_files.emplace(file_number);
492+
}
493+
494+
table_file_levels_[file_number] =
495+
VersionStorageInfo::FileLocation::Invalid().GetLevel();
496+
497+
return Status::OK();
498+
}
499+
500+
Status ApplyFileAddition(int level, const FileMetaData& meta) {
501+
assert(level != VersionStorageInfo::FileLocation::Invalid().GetLevel());
502+
503+
const uint64_t file_number = meta.fd.GetNumber();
504+
505+
const int current_level = GetCurrentLevelForTableFile(file_number);
506+
507+
if (current_level !=
508+
VersionStorageInfo::FileLocation::Invalid().GetLevel()) {
509+
if (level >= num_levels_) {
510+
has_invalid_levels_ = true;
511+
}
512+
513+
std::ostringstream oss;
514+
oss << "Cannot add table file #" << file_number << " to level " << level
515+
<< " since it is already in the LSM tree on level " << current_level;
516+
return Status::Corruption("VersionBuilder", oss.str());
517+
}
518+
519+
if (level >= num_levels_) {
520+
++invalid_level_sizes_[level];
521+
table_file_levels_[file_number] = level;
522+
523+
return Status::OK();
524+
}
525+
526+
auto& level_state = levels_[level];
527+
528+
auto& del_files = level_state.deleted_files;
529+
auto del_it = del_files.find(file_number);
530+
if (del_it != del_files.end()) {
531+
del_files.erase(del_it);
532+
} else {
533+
FileMetaData* const f = new FileMetaData(meta);
534+
f->refs = 1;
535+
536+
auto& add_files = level_state.added_files;
537+
assert(add_files.find(file_number) == add_files.end());
538+
add_files.emplace(file_number, f);
539+
}
540+
541+
table_file_levels_[file_number] = level;
542+
543+
return Status::OK();
544+
}
545+
482546
// Apply all of the edits in *edit to the current state.
483547
Status Apply(VersionEdit* edit) {
484-
Status s = CheckConsistency(base_vstorage_);
485-
if (!s.ok()) {
486-
return s;
548+
{
549+
const Status s = CheckConsistency(base_vstorage_);
550+
if (!s.ok()) {
551+
return s;
552+
}
487553
}
488554

489555
// Delete files
490-
const auto& del = edit->GetDeletedFiles();
491-
for (const auto& del_file : del) {
492-
const auto level = del_file.first;
493-
const auto number = del_file.second;
494-
if (level < num_levels_) {
495-
levels_[level].deleted_files.insert(number);
496-
s = CheckConsistencyForDeletes(edit, number, level);
497-
if (!s.ok()) {
498-
return s;
499-
}
556+
for (const auto& deleted_file : edit->GetDeletedFiles()) {
557+
const int level = deleted_file.first;
558+
const uint64_t file_number = deleted_file.second;
500559

501-
auto exising = levels_[level].added_files.find(number);
502-
if (exising != levels_[level].added_files.end()) {
503-
UnrefFile(exising->second);
504-
levels_[level].added_files.erase(exising);
505-
}
506-
} else {
507-
if (invalid_levels_[level].erase(number) == 0) {
508-
// Deleting an non-existing file on invalid level.
509-
has_invalid_levels_ = true;
510-
}
560+
const Status s = ApplyFileDeletion(level, file_number);
561+
if (!s.ok()) {
562+
return s;
511563
}
512564
}
513565

514566
// Add new files
515567
for (const auto& new_file : edit->GetNewFiles()) {
516568
const int level = new_file.first;
517-
if (level < num_levels_) {
518-
FileMetaData* f = new FileMetaData(new_file.second);
519-
f->refs = 1;
520-
521-
assert(levels_[level].added_files.find(f->fd.GetNumber()) ==
522-
levels_[level].added_files.end());
523-
levels_[level].deleted_files.erase(f->fd.GetNumber());
524-
levels_[level].added_files[f->fd.GetNumber()] = f;
525-
} else {
526-
uint64_t number = new_file.second.fd.GetNumber();
527-
auto& lvls = invalid_levels_[level];
528-
if (lvls.count(number) == 0) {
529-
lvls.insert(number);
530-
} else {
531-
// Creating an already existing file on invalid level.
532-
has_invalid_levels_ = true;
533-
}
569+
const FileMetaData& meta = new_file.second;
570+
571+
const Status s = ApplyFileAddition(level, meta);
572+
if (!s.ok()) {
573+
return s;
534574
}
535575
}
536576

537577
// Add new blob files
538578
for (const auto& blob_file_addition : edit->GetBlobFileAdditions()) {
539-
s = ApplyBlobFileAddition(blob_file_addition);
579+
const Status s = ApplyBlobFileAddition(blob_file_addition);
540580
if (!s.ok()) {
541581
return s;
542582
}
543583
}
544584

545585
// Increase the amount of garbage for blob files affected by GC
546586
for (const auto& blob_file_garbage : edit->GetBlobFileGarbages()) {
547-
s = ApplyBlobFileGarbage(blob_file_garbage);
587+
const Status s = ApplyBlobFileGarbage(blob_file_garbage);
548588
if (!s.ok()) {
549589
return s;
550590
}
551591
}
552592

553-
return s;
593+
return Status::OK();
554594
}
555595

556596
static std::shared_ptr<BlobFileMetaData> CreateMetaDataForNewBlobFile(

0 commit comments

Comments
 (0)