Add fsync / corrupt simulation to env_mem

Summary: as title

Test Plan: env_mem_test

Reviewers: sdong, yhchiang, rven, igor

Reviewed By: igor

Subscribers: dhruba, leveldb

Differential Revision: https://reviews.facebook.net/D28077
This commit is contained in:
Lei Jin 2014-10-31 15:16:31 -07:00
parent 0e526eb9d7
commit 72cb7cf201
3 changed files with 77 additions and 3 deletions

View File

@ -12,13 +12,16 @@
#include <algorithm> #include <algorithm>
#include <chrono> #include <chrono>
#include "util/rate_limiter.h" #include "util/rate_limiter.h"
#include "util/random.h"
#include "util/murmurhash.h"
namespace rocksdb { namespace rocksdb {
class MemFile { class MemFile {
public: public:
explicit MemFile(const std::string& fn) : explicit MemFile(const std::string& fn) :
fn_(fn), refs_(0), size_(0), modified_time_(Now()) {} fn_(fn), refs_(0), size_(0), modified_time_(Now()),
rnd_((uint32_t)MurmurHash(fn.data(), fn.size(), 0)), fsynced_bytes_(0) {}
void Ref() { void Ref() {
MutexLock lock(&mutex_); MutexLock lock(&mutex_);
@ -53,6 +56,19 @@ class MemFile {
} }
} }
void CorruptBuffer() {
if (fsynced_bytes_ >= size_) {
return;
}
uint64_t buffered_bytes = size_ - fsynced_bytes_;
uint64_t start = fsynced_bytes_ + rnd_.Uniform(buffered_bytes);
uint64_t end = std::min(start + 512, size_.load());
MutexLock lock(&mutex_);
for (uint64_t pos = start; pos < end; ++pos) {
data_[pos] = static_cast<char>(rnd_.Uniform(256));
}
}
Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const { Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const {
MutexLock lock(&mutex_); MutexLock lock(&mutex_);
if (offset > Size()) { if (offset > Size()) {
@ -84,6 +100,7 @@ class MemFile {
} }
Status Fsync() { Status Fsync() {
fsynced_bytes_ = size_.load();
return Status::OK(); return Status::OK();
} }
@ -110,9 +127,14 @@ class MemFile {
mutable port::Mutex mutex_; mutable port::Mutex mutex_;
int refs_; int refs_;
// Data written into this file, all bytes before fsynced_bytes are
// persistent.
std::string data_; std::string data_;
std::atomic<uint64_t> size_; std::atomic<uint64_t> size_;
std::atomic<uint64_t> modified_time_; std::atomic<uint64_t> modified_time_;
Random rnd_;
std::atomic<uint64_t> fsynced_bytes_;
}; };
namespace { namespace {
@ -197,7 +219,7 @@ class WritableFileImpl : public WritableFile {
} }
virtual Status Close() { virtual Status Close() {
return Status::OK(); return file_->Fsync();
} }
virtual Status Flush() { virtual Status Flush() {
@ -593,6 +615,17 @@ Status MockEnv::Truncate(const std::string& fname, size_t size) {
return Status::OK(); return Status::OK();
} }
Status MockEnv::CorruptBuffer(const std::string& fname) {
auto fn = NormalizePath(fname);
MutexLock lock(&mutex_);
auto iter = file_map_.find(fn);
if (iter == file_map_.end()) {
return Status::IOError(fn, "File not found");
}
iter->second->CorruptBuffer();
return Status::OK();
}
std::string MockEnv::NormalizePath(const std::string path) { std::string MockEnv::NormalizePath(const std::string path) {
std::string dst; std::string dst;
for (auto c : path) { for (auto c : path) {

View File

@ -81,6 +81,8 @@ class MockEnv : public EnvWrapper {
// Non-virtual functions, specific to MockEnv // Non-virtual functions, specific to MockEnv
Status Truncate(const std::string& fname, size_t size); Status Truncate(const std::string& fname, size_t size);
Status CorruptBuffer(const std::string& fname);
private: private:
std::string NormalizePath(const std::string path); std::string NormalizePath(const std::string path);

View File

@ -182,6 +182,45 @@ TEST(MockEnvTest, LargeWrite) {
delete [] scratch; delete [] scratch;
} }
TEST(MockEnvTest, Corrupt) {
const std::string kGood = "this is a good string, synced to disk";
const std::string kCorrupted = "this part may be corrupted";
const std::string kFileName = "/dir/f";
unique_ptr<WritableFile> writable_file;
ASSERT_OK(env_->NewWritableFile(kFileName, &writable_file, soptions_));
ASSERT_OK(writable_file->Append(kGood));
ASSERT_TRUE(writable_file->GetFileSize() == kGood.size());
std::string scratch;
scratch.resize(kGood.size() + kCorrupted.size() + 16);
Slice result;
unique_ptr<RandomAccessFile> rand_file;
ASSERT_OK(env_->NewRandomAccessFile(kFileName, &rand_file, soptions_));
ASSERT_OK(rand_file->Read(0, kGood.size(), &result, &(scratch[0])));
ASSERT_EQ(result.compare(kGood), 0);
// Sync + corrupt => no change
ASSERT_OK(writable_file->Fsync());
ASSERT_OK(dynamic_cast<MockEnv*>(env_)->CorruptBuffer(kFileName));
result.clear();
ASSERT_OK(rand_file->Read(0, kGood.size(), &result, &(scratch[0])));
ASSERT_EQ(result.compare(kGood), 0);
// Add new data and corrupt it
ASSERT_OK(writable_file->Append(kCorrupted));
ASSERT_TRUE(writable_file->GetFileSize() == kGood.size() + kCorrupted.size());
result.clear();
ASSERT_OK(rand_file->Read(kGood.size(), kCorrupted.size(),
&result, &(scratch[0])));
ASSERT_EQ(result.compare(kCorrupted), 0);
// Corrupted
ASSERT_OK(dynamic_cast<MockEnv*>(env_)->CorruptBuffer(kFileName));
result.clear();
ASSERT_OK(rand_file->Read(kGood.size(), kCorrupted.size(),
&result, &(scratch[0])));
ASSERT_NE(result.compare(kCorrupted), 0);
}
TEST(MockEnvTest, DBTest) { TEST(MockEnvTest, DBTest) {
Options options; Options options;
options.create_if_missing = true; options.create_if_missing = true;