--- 摄于 2017 年 9 月 藏川线前段
ps:感谢排序,让我远离取名烦恼
上一次说到研究方向,总体而言,分两个大方向:找到并修复潜在的问题和优化。这一篇,就说说找到的问题,这也是为什么会卡顿、同步无法正常进行的直接原因。
首先,我们必须了解 ckb 到底是怎么同步的,即正常的逻辑,然后才能对照出异常的问题点。ckb 本身的同步协议与 Bitcoin 的 header-first 模式比较类似,但细节上有一些不一样的地方。笼统地说,ckb 同步分为两个阶段:
节点在 IBD 状态时,relay 协议是不工作的,即不响应新块广播、不转发 Transaction,并且不响应同步协议的被同步请求,一心一意只做同步工作,争取在消除各种干扰的前提下,尽可能快地同步完成。
无论本地客户端从什么状态开始同步,同步的整体逻辑都是一致的:
get_headers
消息给随机节点headers
消息在内存中构建 header_map
,并记录可能的 best_known_header
get_block
消息block
消息,验证通过后更新本地数据库状态,同步更新对端节点状态best_known
等于本地 tip,同步结束上诉流程,其实并没有什么特别的,但这里有几个技术细节实现有问题,导致了卡顿、无法同步,并且这里的问题都不是正确性问题,而是性能问题。我们根据问题进行分类说明,并给出修复的 PR 链接。
相关修复 PR:
首先,我们得理解这个 header map 是个什么东西,准确地讲,它是一个构建在内存的 skip map,也就是它是以 header hash 为 key,header + option point 为 value 的一个 hashmap,可能存在的 point 指向相对较远的 header,可能减少遍历的消耗,包括 io、hash 计算等
它在构建时的跳转逻辑如下:
fn get_skip_height(height: u64) -> u64 {
// Turn the lowest '1' bit in the binary representation of a number into a '0'.
fn invert_lowest_one(n: i64) -> i64 {
n & (n - 1)
}
if height < 2 {
return 0;
}
// Determine which height to jump back to. Any number strictly lower than height is acceptable,
// but the following expression seems to perform well in simulations (max 110 steps to go back
// up to 2**18 blocks).
if (height & 1) > 0 {
invert_lowest_one(invert_lowest_one(height as i64 - 1)) as u64 + 1
} else {
invert_lowest_one(height as i64) as u64
}
}
如果计算一下 get_skip_height(1048577)
这个值,你会很惊讶地发现,这个跳转点在 1 高度,如果内存里有该 header 存在,就只是一个 hash get 的消耗,但如果该值已经被清理掉了,依据 get_ancestor
的遍历逻辑,它需求访问 100w 多次 db 才能找到这个值,100w 次 io 的操作,在外部看来,这就是卡死了,而根据该算法的逻辑,每隔一段高度都会有这类跳转超长的数据存在,在没有把 header map dump 到数据库并会清理内存中的 header map的前提下,这种问题出现的概率是极高的。同样的问题也发生在读取上。
相关修复 PR:
这个问题涉及的背景相对多一点,但理解该问题是很简单的事情。简单的说,在请求 block 消息发送的时候,需要从记录的与该节点的 last common header 开始,根据 header map 指引,找到没有请求的 block,然后发送。这里依赖 last common header 标记的正确性,以及及时性。
举个简单的例子,如果当前 tip 在 100000,对端 last common header 标记在 10,那么在找发送请求点的时候,就需要从 10 遍历到 100000,然后再开始真正的查找工作,一大段无效工作几乎每个节点的请求发出都需要重复一遍,就是因为该值的标记问题。
而真正导致这个问题严重的背景有三个:
尤其是第二条,导致了在查找的时候无法继续接受块信息,造成了卡死的现象。
在 0.31 的 release note 里,除了上诉问题,还有一些修复和优化,比如开启并行下载、修复 RocksDB 的 snapshot 使用问题,这些也与同步相关,但在这一篇就不展开讲解了。
这一篇,讲的是同步问题所在,解决这些问题,卡顿的现象理论上就不存在了,但同步速度并没有达到最佳状态,下一篇讲讲优化问题。
请登录后评论
评论区
加载更多