BlueFS用来存储RocksDB相关的file文件,在故障时需要将BlueFS在内存中重建出来,供RocksDB使用。本文主要对replay流程进行分析。
阅读本文前建议阅读Ceph BlueStore:BlueFS架构介绍 +元数据详解,了解持久化和内存元数据的基本架构
回放步骤包括:
int BlueFS::mount()
{
...
int r = _open_super(); // 恢复superblock
...
r = _replay(false, false); // replay journal
...
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
...
}
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
}
template<class T>
inline void encode_raw(const T& t, bufferlist& bl)
{
bl.append((char*)&t, sizeof(t));
}
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