Fix flakiness of WalManagerTest
Summary: We should use mocked-out env for these tests to make it more realiable. Added benefit is that instead of actually sleeping for 3 seconds, we can instead pretend to sleep and just increase time counters. Test Plan: for i in `seq 100`; do ./wal_manager_test --gtest_filter=WalManagerTest.WALArchivalTtl ;done Reviewers: rven, meyering Reviewed By: meyering Subscribers: meyering, dhruba, leveldb Differential Revision: https://reviews.facebook.net/D36951
This commit is contained in:
parent
d41a565a4a
commit
9b983befa8
@ -14,6 +14,7 @@
|
|||||||
#include "db/column_family.h"
|
#include "db/column_family.h"
|
||||||
#include "db/version_set.h"
|
#include "db/version_set.h"
|
||||||
#include "db/writebuffer.h"
|
#include "db/writebuffer.h"
|
||||||
|
#include "util/mock_env.h"
|
||||||
#include "util/string_util.h"
|
#include "util/string_util.h"
|
||||||
#include "util/testharness.h"
|
#include "util/testharness.h"
|
||||||
#include "util/testutil.h"
|
#include "util/testutil.h"
|
||||||
@ -27,7 +28,7 @@ namespace rocksdb {
|
|||||||
class WalManagerTest : public testing::Test {
|
class WalManagerTest : public testing::Test {
|
||||||
public:
|
public:
|
||||||
WalManagerTest()
|
WalManagerTest()
|
||||||
: env_(Env::Default()),
|
: env_(new MockEnv(Env::Default())),
|
||||||
dbname_(test::TmpDir() + "/wal_manager_test"),
|
dbname_(test::TmpDir() + "/wal_manager_test"),
|
||||||
table_cache_(NewLRUCache(50000, 16)),
|
table_cache_(NewLRUCache(50000, 16)),
|
||||||
write_buffer_(db_options_.db_write_buffer_size),
|
write_buffer_(db_options_.db_write_buffer_size),
|
||||||
@ -41,6 +42,7 @@ class WalManagerTest : public testing::Test {
|
|||||||
db_options_.db_paths.emplace_back(dbname_,
|
db_options_.db_paths.emplace_back(dbname_,
|
||||||
std::numeric_limits<uint64_t>::max());
|
std::numeric_limits<uint64_t>::max());
|
||||||
db_options_.wal_dir = dbname_;
|
db_options_.wal_dir = dbname_;
|
||||||
|
db_options_.env = env_.get();
|
||||||
|
|
||||||
versions_.reset(new VersionSet(dbname_, &db_options_, env_options_,
|
versions_.reset(new VersionSet(dbname_, &db_options_, env_options_,
|
||||||
table_cache_.get(), &write_buffer_,
|
table_cache_.get(), &write_buffer_,
|
||||||
@ -91,7 +93,7 @@ class WalManagerTest : public testing::Test {
|
|||||||
return std::move(iter);
|
return std::move(iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
Env* env_;
|
std::unique_ptr<MockEnv> env_;
|
||||||
std::string dbname_;
|
std::string dbname_;
|
||||||
WriteController write_controller_;
|
WriteController write_controller_;
|
||||||
EnvOptions env_options_;
|
EnvOptions env_options_;
|
||||||
@ -212,22 +214,22 @@ TEST_F(WalManagerTest, WALArchivalSizeLimit) {
|
|||||||
CreateArchiveLogs(20, 5000);
|
CreateArchiveLogs(20, 5000);
|
||||||
|
|
||||||
std::vector<std::uint64_t> log_files =
|
std::vector<std::uint64_t> log_files =
|
||||||
ListSpecificFiles(env_, archive_dir, kLogFile);
|
ListSpecificFiles(env_.get(), archive_dir, kLogFile);
|
||||||
ASSERT_EQ(log_files.size(), 20U);
|
ASSERT_EQ(log_files.size(), 20U);
|
||||||
|
|
||||||
db_options_.WAL_size_limit_MB = 8;
|
db_options_.WAL_size_limit_MB = 8;
|
||||||
Reopen();
|
Reopen();
|
||||||
wal_manager_->PurgeObsoleteWALFiles();
|
wal_manager_->PurgeObsoleteWALFiles();
|
||||||
|
|
||||||
uint64_t archive_size = GetLogDirSize(archive_dir, env_);
|
uint64_t archive_size = GetLogDirSize(archive_dir, env_.get());
|
||||||
ASSERT_TRUE(archive_size <= db_options_.WAL_size_limit_MB * 1024 * 1024);
|
ASSERT_TRUE(archive_size <= db_options_.WAL_size_limit_MB * 1024 * 1024);
|
||||||
|
|
||||||
db_options_.WAL_ttl_seconds = 1;
|
db_options_.WAL_ttl_seconds = 1;
|
||||||
env_->SleepForMicroseconds(2 * 1000 * 1000);
|
env_->FakeSleepForMicroseconds(2 * 1000 * 1000);
|
||||||
Reopen();
|
Reopen();
|
||||||
wal_manager_->PurgeObsoleteWALFiles();
|
wal_manager_->PurgeObsoleteWALFiles();
|
||||||
|
|
||||||
log_files = ListSpecificFiles(env_, archive_dir, kLogFile);
|
log_files = ListSpecificFiles(env_.get(), archive_dir, kLogFile);
|
||||||
ASSERT_TRUE(log_files.empty());
|
ASSERT_TRUE(log_files.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,15 +247,15 @@ TEST_F(WalManagerTest, WALArchivalTtl) {
|
|||||||
CreateArchiveLogs(20, 5000);
|
CreateArchiveLogs(20, 5000);
|
||||||
|
|
||||||
std::vector<uint64_t> log_files =
|
std::vector<uint64_t> log_files =
|
||||||
ListSpecificFiles(env_, archive_dir, kLogFile);
|
ListSpecificFiles(env_.get(), archive_dir, kLogFile);
|
||||||
ASSERT_GT(log_files.size(), 0U);
|
ASSERT_GT(log_files.size(), 0U);
|
||||||
|
|
||||||
db_options_.WAL_ttl_seconds = 1;
|
db_options_.WAL_ttl_seconds = 1;
|
||||||
env_->SleepForMicroseconds(3 * 1000 * 1000);
|
env_->FakeSleepForMicroseconds(3 * 1000 * 1000);
|
||||||
Reopen();
|
Reopen();
|
||||||
wal_manager_->PurgeObsoleteWALFiles();
|
wal_manager_->PurgeObsoleteWALFiles();
|
||||||
|
|
||||||
log_files = ListSpecificFiles(env_, archive_dir, kLogFile);
|
log_files = ListSpecificFiles(env_.get(), archive_dir, kLogFile);
|
||||||
ASSERT_TRUE(log_files.empty());
|
ASSERT_TRUE(log_files.empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,9 @@ namespace rocksdb {
|
|||||||
|
|
||||||
class MemFile {
|
class MemFile {
|
||||||
public:
|
public:
|
||||||
explicit MemFile(const std::string& fn, bool _is_lock_file = false)
|
explicit MemFile(Env* env, const std::string& fn, bool _is_lock_file = false)
|
||||||
: fn_(fn),
|
: env_(env),
|
||||||
|
fn_(fn),
|
||||||
refs_(0),
|
refs_(0),
|
||||||
is_lock_file_(_is_lock_file),
|
is_lock_file_(_is_lock_file),
|
||||||
locked_(false),
|
locked_(false),
|
||||||
@ -137,8 +138,10 @@ class MemFile {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t Now() {
|
uint64_t Now() {
|
||||||
return std::chrono::duration_cast<std::chrono::seconds>(
|
int64_t unix_time;
|
||||||
std::chrono::system_clock::now().time_since_epoch()).count();
|
auto s = env_->GetCurrentTime(&unix_time);
|
||||||
|
assert(s.ok());
|
||||||
|
return static_cast<uint64_t>(unix_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Private since only Unref() should be used to delete it.
|
// Private since only Unref() should be used to delete it.
|
||||||
@ -150,6 +153,7 @@ class MemFile {
|
|||||||
MemFile(const MemFile&);
|
MemFile(const MemFile&);
|
||||||
void operator=(const MemFile&);
|
void operator=(const MemFile&);
|
||||||
|
|
||||||
|
Env* env_;
|
||||||
const std::string fn_;
|
const std::string fn_;
|
||||||
mutable port::Mutex mutex_;
|
mutable port::Mutex mutex_;
|
||||||
int refs_;
|
int refs_;
|
||||||
@ -393,8 +397,7 @@ class TestMemLogger : public Logger {
|
|||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
MockEnv::MockEnv(Env* base_env)
|
MockEnv::MockEnv(Env* base_env) : EnvWrapper(base_env), fake_sleep_micros_(0) {}
|
||||||
: EnvWrapper(base_env) {}
|
|
||||||
|
|
||||||
MockEnv::~MockEnv() {
|
MockEnv::~MockEnv() {
|
||||||
for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i) {
|
for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i) {
|
||||||
@ -445,7 +448,7 @@ Status MockEnv::NewWritableFile(const std::string& fname,
|
|||||||
if (file_map_.find(fn) != file_map_.end()) {
|
if (file_map_.find(fn) != file_map_.end()) {
|
||||||
DeleteFileInternal(fn);
|
DeleteFileInternal(fn);
|
||||||
}
|
}
|
||||||
MemFile* file = new MemFile(fn, false);
|
MemFile* file = new MemFile(this, fn, false);
|
||||||
file->Ref();
|
file->Ref();
|
||||||
file_map_[fn] = file;
|
file_map_[fn] = file;
|
||||||
|
|
||||||
@ -599,7 +602,7 @@ Status MockEnv::NewLogger(const std::string& fname,
|
|||||||
auto iter = file_map_.find(fn);
|
auto iter = file_map_.find(fn);
|
||||||
MemFile* file = nullptr;
|
MemFile* file = nullptr;
|
||||||
if (iter == file_map_.end()) {
|
if (iter == file_map_.end()) {
|
||||||
file = new MemFile(fn, false);
|
file = new MemFile(this, fn, false);
|
||||||
file->Ref();
|
file->Ref();
|
||||||
file_map_[fn] = file;
|
file_map_[fn] = file;
|
||||||
} else {
|
} else {
|
||||||
@ -622,7 +625,7 @@ Status MockEnv::LockFile(const std::string& fname, FileLock** flock) {
|
|||||||
return Status::IOError(fn, "Lock is already held.");
|
return Status::IOError(fn, "Lock is already held.");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
auto* file = new MemFile(fn, true);
|
auto* file = new MemFile(this, fn, true);
|
||||||
file->Ref();
|
file->Ref();
|
||||||
file->Lock();
|
file->Lock();
|
||||||
file_map_[fn] = file;
|
file_map_[fn] = file;
|
||||||
@ -652,6 +655,20 @@ Status MockEnv::GetTestDirectory(std::string* path) {
|
|||||||
return Status::OK();
|
return Status::OK();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Status MockEnv::GetCurrentTime(int64_t* unix_time) {
|
||||||
|
auto s = EnvWrapper::GetCurrentTime(unix_time);
|
||||||
|
*unix_time += fake_sleep_micros_.load() / (1000 * 1000);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t MockEnv::NowMicros() {
|
||||||
|
return EnvWrapper::NowMicros() + fake_sleep_micros_.load();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t MockEnv::NowNanos() {
|
||||||
|
return EnvWrapper::NowNanos() + fake_sleep_micros_.load() * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
// Non-virtual functions, specific to MockEnv
|
// Non-virtual functions, specific to MockEnv
|
||||||
Status MockEnv::Truncate(const std::string& fname, size_t size) {
|
Status MockEnv::Truncate(const std::string& fname, size_t size) {
|
||||||
auto fn = NormalizePath(fname);
|
auto fn = NormalizePath(fname);
|
||||||
@ -686,4 +703,8 @@ std::string MockEnv::NormalizePath(const std::string path) {
|
|||||||
return dst;
|
return dst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MockEnv::FakeSleepForMicroseconds(int64_t micros) {
|
||||||
|
fake_sleep_micros_.fetch_add(micros);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace rocksdb
|
} // namespace rocksdb
|
||||||
|
@ -82,11 +82,20 @@ class MockEnv : public EnvWrapper {
|
|||||||
|
|
||||||
virtual Status GetTestDirectory(std::string* path) override;
|
virtual Status GetTestDirectory(std::string* path) override;
|
||||||
|
|
||||||
|
// Results of these can be affected by FakeSleepForMicroseconds()
|
||||||
|
virtual Status GetCurrentTime(int64_t* unix_time) override;
|
||||||
|
virtual uint64_t NowMicros() override;
|
||||||
|
virtual uint64_t NowNanos() override;
|
||||||
|
|
||||||
// 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);
|
Status CorruptBuffer(const std::string& fname);
|
||||||
|
|
||||||
|
// Doesn't really sleep, just affects output of GetCurrentTime(), NowMicros()
|
||||||
|
// and NowNanos()
|
||||||
|
void FakeSleepForMicroseconds(int64_t micros);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::string NormalizePath(const std::string path);
|
std::string NormalizePath(const std::string path);
|
||||||
|
|
||||||
@ -94,6 +103,8 @@ class MockEnv : public EnvWrapper {
|
|||||||
typedef std::map<std::string, MemFile*> FileSystem;
|
typedef std::map<std::string, MemFile*> FileSystem;
|
||||||
port::Mutex mutex_;
|
port::Mutex mutex_;
|
||||||
FileSystem file_map_; // Protected by mutex_.
|
FileSystem file_map_; // Protected by mutex_.
|
||||||
|
|
||||||
|
std::atomic<int64_t> fake_sleep_micros_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rocksdb
|
} // namespace rocksdb
|
||||||
|
@ -15,7 +15,7 @@ namespace rocksdb {
|
|||||||
|
|
||||||
class MockEnvTest : public testing::Test {
|
class MockEnvTest : public testing::Test {
|
||||||
public:
|
public:
|
||||||
Env* env_;
|
MockEnv* env_;
|
||||||
const EnvOptions soptions_;
|
const EnvOptions soptions_;
|
||||||
|
|
||||||
MockEnvTest()
|
MockEnvTest()
|
||||||
@ -264,6 +264,19 @@ TEST_F(MockEnvTest, DBTest) {
|
|||||||
delete db;
|
delete db;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(MockEnvTest, FakeSleeping) {
|
||||||
|
int64_t now = 0;
|
||||||
|
auto s = env_->GetCurrentTime(&now);
|
||||||
|
ASSERT_OK(s);
|
||||||
|
env_->FakeSleepForMicroseconds(3 * 1000 * 1000);
|
||||||
|
int64_t after_sleep = 0;
|
||||||
|
s = env_->GetCurrentTime(&after_sleep);
|
||||||
|
ASSERT_OK(s);
|
||||||
|
auto delta = after_sleep - now;
|
||||||
|
// this will be true unless test runs for 2 seconds
|
||||||
|
ASSERT_TRUE(delta == 3 || delta == 4);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace rocksdb
|
} // namespace rocksdb
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user