前言
之前写了一些东西,整体了解了工程的编译脚本和一个test的工程结构,本来想按部就班,慢慢来解决可是咱们还是直接进入主题,直接啃这个数据库的测试程序,看看到底如何实现,
正文
我们直接进入db_test.cpp
文件,找到第一个test类。
TEST(DBTest, Empty) {
do {
ASSERT_TRUE(db_ != NULL);
ASSERT_EQ("NOT_FOUND", Get("foo"));
} while (ChangeOptions());
}
这个测试其实很简单,就是简单的额测试真实的操作的db_
代理对象是不是为空,和查找一个键值为kfoo
的值。这里我们必须要找到初始化的代码,看看如何获取代理对象,并且初始化这个类的。
DBTest() : option_config_(kDefault),
env_(new SpecialEnv(Env::Default())) {
filter_policy_ = NewBloomFilterPolicy(10);
dbname_ = test::TmpDir() + "/db_test";
DestroyDB(dbname_, Options());
db_ = NULL;
Reopen();
}
这里我们记住初始化了几个变量,这里暂时不详细介绍,关于dbname_
,这个我们稍微介绍下,具体获取代码如下
virtual Status GetTestDirectory(std::string* result) {
const char* env = getenv("TEST_TMPDIR");
if (env && env[0] != '\0') {
*result = env;
} else {
char buf[100];
snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid()));
*result = buf;
}
// Directory may already exist
CreateDir(*result);
return Status::OK();
}
static void InitDefaultEnv() { default_env = new PosixEnv; }
Env* Env::Default() {
pthread_once(&once, InitDefaultEnv);
return default_env;
}
std::string TmpDir() {
std::string dir;
Status s = Env::Default()->GetTestDirectory(&dir);
ASSERT_TRUE(s.ok()) << s.ToString();
return dir;
}
这里的getenv("TEST_TMPDIR")
为空,所以基本就是/tmp/leveldbtest-%d
,这里根据uid得到唯一的一个数据库。不过觉得,因为单次,可以删除自己,所以肯定不会重合。Env::Default()
这个其实是防止并发,这里返回时一个PosixEnv
,这是一个类似代理模式的设计,这里就不详细介绍,其实就是为了得到一个唯一的文件目录。
关键是最后三句话,这才是初始化db_的语句,我们好好看看:
Status DestroyDB(const std::string& dbname, const Options& options) {
Env* env = options.env;
std::vector<std::string> filenames;
// Ignore error in case directory does not exist
env->GetChildren(dbname, &filenames);
if (filenames.empty()) {
return Status::OK();
}
FileLock* lock;
const std::string lockname = LockFileName(dbname);
Status result = env->LockFile(lockname, &lock);
if (result.ok()) {
uint64_t number;
FileType type;
for (size_t i = 0; i < filenames.size(); i++) {
if (ParseFileName(filenames[i], &number, &type) &&
type != kDBLockFile) { // Lock file will be deleted at end
Status del = env->DeleteFile(dbname + "/" + filenames[i]);
if (result.ok() && !del.ok()) {
result = del;
}
}
}
env->UnlockFile(lock); // Ignore error since state is already gone
env->DeleteFile(lockname);
env->DeleteDir(dbname); // Ignore error in case dir contains other files
}
return result;
}
传入的参数是个Options的结构体。这里保存一个Env::Default()结构。关于这里删除必要的文件,这里加锁防止并发,并且具有保护措施,其实挺好的,这里就不详细介绍,有兴趣的可以自己阅读。
Status TryReopen(Options* options) {
delete db_;
db_ = NULL;
Options opts;
if (options != NULL) {
opts = *options;
} else {
opts = CurrentOptions();
opts.create_if_missing = true;
}
last_options_ = opts;
return DB::Open(opts, dbname_, &db_);
}
Status DB::Open(const Options& options, const std::string& dbname,
DB** dbptr) {
*dbptr = NULL;
//这里是常见一个工作类。
DBImpl* impl = new DBImpl(options, dbname);
impl->mutex_.Lock();
VersionEdit edit;
// Recover handles create_if_missing, error_if_exists
bool save_manifest = false;
Status s = impl->Recover(&edit, &save_manifest);
if (s.ok() && impl->mem_ == NULL) {
// Create new log and a corresponding memtable.
uint64_t new_log_number = impl->versions_->NewFileNumber();
WritableFile* lfile;
s = options.env->NewWritableFile(LogFileName(dbname, new_log_number),
&lfile);
if (s.ok()) {
edit.SetLogNumber(new_log_number);
impl->logfile_ = lfile;
impl->logfile_number_ = new_log_number;
impl->log_ = new log::Writer(lfile);
impl->mem_ = new MemTable(impl->internal_comparator_);
impl->mem_->Ref();
}
}
if (s.ok() && save_manifest) {
edit.SetPrevLogNumber(0); // No older logs needed after recovery.
edit.SetLogNumber(impl->logfile_number_);
s = impl->versions_->LogAndApply(&edit, &impl->mutex_);
}
if (s.ok()) {
impl->DeleteObsoleteFiles();
impl->MaybeScheduleCompaction();
}
impl->mutex_.Unlock();
if (s.ok()) {
assert(impl->mem_ != NULL);
*dbptr = impl;
} else {
delete impl;
}
return s;
}
这里其实就是调用了Open
函数,这里才是初始化整个数据库的核心地方,这里因为我们还没有存入数据,所以这里仅仅初始化了一个log的文件,但是经过观察 ,貌似还没创建文件,这里就不详细介绍了。
也就是高了一个DBImpl
类,然后就没有然后了。这里初始化基本完成了,还剩下一个最终的问题,
ASSERT_TRUE(db_ != NULL);
ASSERT_EQ("NOT_FOUND", Get("foo"));
这里很容易搞懂啦!第一个就不用多说,第二个,我们看下
std::string Get(const std::string& k, const Snapshot* snapshot = NULL) {
ReadOptions options;
options.snapshot = snapshot;
std::string result;
Status s = db_->Get(options, k, &result);
if (s.IsNotFound()) {
result = "NOT_FOUND";
} else if (!s.ok()) {
result = s.ToString();
}
return result;
}
这里我们很容易知道取得当然是空,我们这里不详细介绍获取的内容,因为我们还没阅读添加字段的代码,阅读这里觉得没太大意义。
后记
有是一天,又是半篇,进度总是没自己想的快,不过至少还在前行,希望快速阅读完这个代码,带着问题,去看下书,解决下自己的语法的问题,希望自己快速掌握c++。加油。