leveldb源码分析四

前言

之前写了一些东西,整体了解了工程的编译脚本和一个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++。加油。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值