Ceph BlueStore:BlueFS元数据恢复replay流程详解 + 元数据encode/decode

文章目录

  • 引言
    • BlueFS
    • 本文结论
  • BlueFS元数据replay代码
    • BlueFS内存元数据replay代码
      • 函数入口:BlueFS::mount
      • 回放superblock:BlueFS::_open_super
      • 回放journal:BlueFS::_replay
    • 元数据encode/decode函数
      • encode
      • decode
  • 参考文献

引言

BlueFS

BlueFS用来存储RocksDB相关的file文件,在故障时需要将BlueFS在内存中重建出来,供RocksDB使用。本文主要对replay流程进行分析。
阅读本文前建议阅读Ceph BlueStore:BlueFS架构介绍 +元数据详解,了解持久化和内存元数据的基本架构

本文结论

  • 回放步骤
    • 回放superblock:默认为BlueFS中第二个block。
    • 回放journal:根据superblock找到journal file,遍历journal file中每个transaction恢复dir_map和file_map等内存元数据。

BlueFS元数据replay代码

BlueFS内存元数据replay代码

函数入口:BlueFS::mount

回放步骤包括:

  • 回放superblock:默认为BlueFS中第二个block。
  • 回放journal:根据superblock找到journal file,遍历journal file中每个transaction恢复dir_map和file_map等内存元数据。
int BlueFS::mount()
{
	...
  	int r = _open_super(); // 恢复superblock
  	...
  	r = _replay(false, false); // replay journal
  	...

回放superblock:BlueFS::_open_super

int BlueFS::_open_super()
{
	...
	// 读取superblock,默认为第二个block
  	r = bdev[BDEV_DB]->read(get_super_offset(), get_super_length(),
			  &bl, ioc[BDEV_DB], false);
	...
  	decode(super, p); // 解码出bluefs_super_t
  	...
  	crc = t.crc32c(-1); // 根据解码出的bluefs_super_t计算crc
	...
  	decode(expected_crc, p); // 解码encode过程中计算的bluefs_super_t crc,并与前面计算的crc比较。
  	...
}

superblock的encode在如下函数中:

int BlueFS::_write_super(int dev)
{
  	encode(super, bl); // 先encode bluefs_super_t
  	...
  	encode(crc, bl); // 然后计算crc,并进行encode
  	...
  	bl.append_zero(get_super_length() - bl.length()); // 补齐一个block
	...
  	bdev[dev]->write(get_super_offset(), bl, false, WRITE_LIFE_SHORT); // 写block到device
  	...
}

回放journal:BlueFS::_replay

int BlueFS::_replay(bool noop, bool to_stdout)
{
	...
	// 获取logfile,其中的fnode从superblock中获取。
	log_file = _get_file(1);
    log_file->fnode = super.log_fnode;
	...
	// 配置预读参数,用于进行读加速
	FileReader *log_reader = new FileReader(log_file, cct->_conf->bluefs_max_prefetch, false, true);
	// while循环从log_file中读取数据
	while (true) {
	    uint64_t pos = log_reader->buf.pos;
	    uint64_t read_pos = pos; // 读取位置
	    bufferlist bl;
	    {
	    	// 读取一个block的数据,保存在bl中
			int r = _read(log_reader, &log_reader->buf, read_pos, super.block_size, &bl, NULL);
			read_pos += r;
	    }
		uint64_t more = 0;
		uint64_t seq;
		uuid_d uuid;
		{
			auto p = bl.cbegin();
			__u8 a, b; // 两个占位符,由于ENCODE_START中v和compact占用
			uint32_t len;
			decode(a, p);
			decode(b, p);
			decode(len, p); // 获取一个transaction长度
			decode(uuid, p); // 获取uuid,用于校验与superblock中是否一致
			decode(seq, p);  // 获取logseq,用于娇艳transaction是否连续
			// 判断是否为一个完整的block,如果需要,后面再读一个block上来
			if (len + 6 > bl.length()) {
				more = round_up_to(len + 6 - bl.length(), super.block_size);
			}
		}
		...
		// 如果不完整
		if (more) {
			bufferlist t;
			int r = _read(log_reader, &log_reader->buf, read_pos, more, &t, NULL);
			bl.claim_append(t);
			read_pos += r;
    	}
    	// 遍历读取的bl中所有transaction
    	bluefs_transaction_t t;
		try {
			auto p = bl.cbegin();
			decode(t, p); // 解码出transaction
		}
		...
		// 遍历bluefs_transaction_t中记录的所有op
		auto p = t.op_bl.cbegin();
		while (!p.end()) {
			__u8 op;
			// 解码出op,根据不同的op类型进行相应的解码操作,后文已添加一个file为例。
			decode(op, p);
			switch (op) {
				case bluefs_transaction_t::OP_INIT:
					...
					break;
				...
				case bluefs_transaction_t::OP_DIR_LINK:
				{
					string dirname, filename;
					uint64_t ino;
					decode(dirname, p); // 解码dir name
					decode(filename, p); // 解码file name
					decode(ino, p); // 解码file num
					FileRef file = _get_file(ino); // 在file_map中添加ino
					map<string,DirRef>::iterator q = dir_map.find(dirname); // 在dirmap中搜索dir,dir_map是在OP_DIR_CREATE中恢复出来的
					map<string,FileRef>::iterator r = q->second->file_map.find(filename); // 在dir的filemap中搜索file
					q->second->file_map[filename] = file; // 将file添加到对应dir的file map下。
						++file->refs;
					}
				}
				break;
				...
			} // 处理一个op完成
			...
			++log_seq; // 每个log op回放完成,logseq + 1
			log_file->fnode.size = log_reader->buf.pos; // fnode的size也需要更新
		} // while遍历op
		...
	} // while循环从logfile中读数据
}

与OP_DIR_LINK这个op对应的encode函数如下:

void op_dir_link(const string& dir, const string& file, uint64_t ino) {
	using ceph::encode;
	encode((__u8)OP_DIR_LINK, op_bl); // 编码OP_DIR_LINK
	encode(dir, op_bl); // 编码dirname
	encode(file, op_bl); // 编码filename
	encode(ino, op_bl); // 编码ino
}

元数据encode/decode函数

encode

template<class T>
inline void encode_raw(const T& t, bufferlist& bl)
{
	bl.append((char*)&t, sizeof(t));
}

decode

template<class T>
inline void decode_raw(T& t, bufferlist::const_iterator &p)
{
	p.copy(sizeof(t), (char*)&t);
}

参考文献

Ceph BlueStore:BlueFS架构介绍 +元数据详解
http://blog.wjin.org/posts/ceph-bluestore-bluefs.html

你可能感兴趣的:(Ceph,BlueStore,分布式,云计算,linux,大数据)