Skip to content

Commit e67241f

Browse files
committed
Sanity check on Open
Summary: Everytime a client opens a DB, we do a sanity check that: * checks the existance of all the necessary files * verifies that file sizes are correct Some of the code was stolen from https://reviews.facebook.net/D16935 Test Plan: added a unit test Reviewers: dhruba, haobo, sdong Reviewed By: dhruba CC: leveldb Differential Revision: https://reviews.facebook.net/D17097
1 parent 7981a43 commit e67241f

File tree

7 files changed

+73
-54
lines changed

7 files changed

+73
-54
lines changed

HISTORY.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
* Chagned Options.prefix_extractor from raw pointer to shared_ptr (take ownership)
1414
Changed HashSkipListRepFactory and HashLinkListRepFactory constructor to not take SliceTransform object (use Options.prefix_extractor implicitly)
1515
* Added Env::GetThreadPoolQueueLen(), which returns the waiting queue length of thread pools
16-
* Added DB::CheckConsistency(), which checks the consistency of live files' metadata
17-
Added a corresponding command "checkconsistency" in ldb tool
16+
* Added a command "checkconsistency" in ldb tool, which checks
17+
if file system state matches DB state (file existence and file sizes)
1818

1919
### New Features
2020
* If we find one truncated record at the end of the MANIFEST or WAL files,

db/corruption_test.cc

+32
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,38 @@ TEST(CorruptionTest, UnrelatedKeys) {
376376
ASSERT_EQ(Value(1000, &tmp2).ToString(), v);
377377
}
378378

379+
TEST(CorruptionTest, FileSystemStateCorrupted) {
380+
for (int iter = 0; iter < 2; ++iter) {
381+
Options options;
382+
options.paranoid_checks = true;
383+
options.create_if_missing = true;
384+
Reopen(&options);
385+
Build(10);
386+
ASSERT_OK(db_->Flush(FlushOptions()));
387+
DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);
388+
std::vector<LiveFileMetaData> metadata;
389+
dbi->GetLiveFilesMetaData(&metadata);
390+
ASSERT_GT(metadata.size(), 0);
391+
std::string filename = dbname_ + metadata[0].name;
392+
393+
delete db_;
394+
395+
if (iter == 0) { // corrupt file size
396+
unique_ptr<WritableFile> file;
397+
env_.NewWritableFile(filename, &file, EnvOptions());
398+
file->Append(Slice("corrupted sst"));
399+
file.reset();
400+
} else { // delete the file
401+
env_.DeleteFile(filename);
402+
}
403+
404+
Status x = TryReopen(&options);
405+
ASSERT_TRUE(x.IsCorruption());
406+
DestroyDB(dbname_, options_);
407+
Reopen(&options);
408+
}
409+
}
410+
379411
} // namespace rocksdb
380412

381413
int main(int argc, char** argv) {

db/db_impl.cc

+31-1
Original file line numberDiff line numberDiff line change
@@ -969,6 +969,9 @@ Status DBImpl::Recover(bool read_only, bool error_if_log_file_exist) {
969969
}
970970

971971
Status s = versions_->Recover();
972+
if (options_.paranoid_checks && s.ok()) {
973+
s = CheckConsistency();
974+
}
972975
if (s.ok()) {
973976
SequenceNumber max_sequence(0);
974977

@@ -3828,11 +3831,38 @@ Status DBImpl::DeleteFile(std::string name) {
38283831
return status;
38293832
}
38303833

3831-
void DBImpl::GetLiveFilesMetaData(std::vector<LiveFileMetaData> *metadata) {
3834+
void DBImpl::GetLiveFilesMetaData(std::vector<LiveFileMetaData>* metadata) {
38323835
MutexLock l(&mutex_);
38333836
return versions_->GetLiveFilesMetaData(metadata);
38343837
}
38353838

3839+
Status DBImpl::CheckConsistency() {
3840+
mutex_.AssertHeld();
3841+
std::vector<LiveFileMetaData> metadata;
3842+
versions_->GetLiveFilesMetaData(&metadata);
3843+
3844+
std::string corruption_messages;
3845+
for (const auto& md : metadata) {
3846+
std::string file_path = dbname_ + md.name;
3847+
uint64_t fsize = 0;
3848+
Status s = env_->GetFileSize(file_path, &fsize);
3849+
if (!s.ok()) {
3850+
corruption_messages +=
3851+
"Can't access " + md.name + ": " + s.ToString() + "\n";
3852+
} else if (fsize != md.size) {
3853+
corruption_messages += "Sst file size mismatch: " + md.name +
3854+
". Size recorded in manifest " +
3855+
std::to_string(md.size) + ", actual size " +
3856+
std::to_string(fsize) + "\n";
3857+
}
3858+
}
3859+
if (corruption_messages.size() == 0) {
3860+
return Status::OK();
3861+
} else {
3862+
return Status::Corruption(corruption_messages);
3863+
}
3864+
}
3865+
38363866
void DBImpl::TEST_GetFilesMetaData(
38373867
std::vector<std::vector<FileMetaData>>* metadata) {
38383868
MutexLock l(&mutex_);

db/db_impl.h

+4
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,10 @@ class DBImpl : public DB {
9494
virtual void GetLiveFilesMetaData(
9595
std::vector<LiveFileMetaData> *metadata);
9696

97+
// checks if all live files exist on file system and that their file sizes
98+
// match to our in-memory records
99+
virtual Status CheckConsistency();
100+
97101
virtual Status GetDbIdentity(std::string& identity);
98102

99103
Status RunManualCompaction(int input_level,

db/db_impl_readonly.cc

-42
Original file line numberDiff line numberDiff line change
@@ -99,46 +99,4 @@ Status DB::OpenForReadOnly(const Options& options, const std::string& dbname,
9999
return s;
100100
}
101101

102-
Status DB::CheckConsistency(const Options& options,
103-
const std::string& name) {
104-
DB *db = nullptr;
105-
Status st;
106-
107-
st = DB::OpenForReadOnly(options, name, &db);
108-
if (!st.ok()) {
109-
return st;
110-
}
111-
112-
std::vector<LiveFileMetaData> metadata;
113-
db->GetLiveFilesMetaData(&metadata);
114-
115-
for (const auto& md : metadata) {
116-
std::string file_path = name + md.name;
117-
118-
if (!db->GetEnv()->FileExists(file_path)) {
119-
st = Status::Corruption("sst file " + md.name + " doesn't exist");
120-
break;
121-
}
122-
123-
uint64_t fsize = 0;
124-
st = db->GetEnv()->GetFileSize(file_path, &fsize);
125-
if (!st.ok()) {
126-
st = Status::Corruption(
127-
"Failed to determine the actual size of file " + md.name +
128-
": " + st.ToString());
129-
break;
130-
}
131-
if (fsize != md.size) {
132-
st = Status::Corruption(
133-
"sst file size mismatch: " + md.name +
134-
". Size recorded in manifest " + std::to_string(md.size) +
135-
", actual size " + std::to_string(fsize));
136-
break;
137-
}
138-
}
139-
140-
delete db;
141-
return st;
142-
}
143-
144102
} // namespace rocksdb

include/rocksdb/db.h

-8
Original file line numberDiff line numberDiff line change
@@ -91,14 +91,6 @@ class DB {
9191
const std::string& name, DB** dbptr,
9292
bool error_if_log_file_exist = false);
9393

94-
// Check the consistency of live files' metadata.
95-
// It will return Corruption Status when a file in manifest
96-
// doesn't actually exist or doesn't match the actual file size.
97-
// Note: This call should be invoked only when the database is
98-
// not already open and serving data.
99-
static Status CheckConsistency(const Options& options,
100-
const std::string& name);
101-
10294
DB() { }
10395
virtual ~DB();
10496

util/ldb_cmd.cc

+4-1
Original file line numberDiff line numberDiff line change
@@ -1765,10 +1765,13 @@ void CheckConsistencyCommand::Help(string& ret) {
17651765

17661766
void CheckConsistencyCommand::DoCommand() {
17671767
Options opt = PrepareOptionsForOpenDB();
1768+
opt.paranoid_checks = true;
17681769
if (!exec_state_.IsNotStarted()) {
17691770
return;
17701771
}
1771-
Status st = DB::CheckConsistency(opt, db_path_);
1772+
DB* db;
1773+
Status st = DB::OpenForReadOnly(opt, db_path_, &db, false);
1774+
delete db;
17721775
if (st.ok()) {
17731776
fprintf(stdout, "OK\n");
17741777
} else {

0 commit comments

Comments
 (0)