Skip to main content
consistency-checker 不在 leafage-evm 的数据路径上——它消费与 leafage-evm 相同的 Kafka 通知,观察副本状态,然后做三件事:向外部消费者发布已确认的区块通知、在 S3 上标记分叉块、把节点健康状态写进 etcd。

它解决什么问题

写节点发布通知的那一刻,副本还没有应用这个区块。外部消费者如果直接订阅内部 topic,就会在 leafage-evm 还查不到该区块时收到通知。 checker 在中间加了一道确认:等到足够比例的副本确实追上了这个高度,才向外部 topic 发布。外部消费者因此得到一个保证——收到通知时,去查询集群一定能读到这个区块。 顺带解决的两件事:分叉块在 S3 上没有标记(谁是规范链只有观察者知道),以及 nodex-proxy 需要知道每个节点当前是否健康。

处理流程

核心实现在 check/check.goProcess。几个设计要点:
S3 预取和副本轮询同时进行。轮询通常要几十到几百毫秒,正好覆盖 S3 往返;失败路径不等预取结束,goroutine 自行收尾。
整条 Process 失败时不提交 Kafka offset,下一轮重试同一条消息。重复消息、以及“已处理到发布步骤但整条未提交”的情况会短路到对齐后直接推进,避免死锁重试。已成功投递的目的地记入 delivered,重试时跳过,因此既不重复投递也不漏投递。
check_interval_ms(默认 20ms)为间隔轮询所有副本的 eth_blockNumber,直到 ready_ratio(默认 0.8)的副本达到目标高度。总时长上限是 check_timeout_ms(默认 2000ms),单次 RPC 上限是 rpc_node_timeout_ms(默认 5000ms)。超时判失败,Process 整体重试。配置项 check_num 已废弃,不再生效。
预取的同高度键列表是 Process 开头的快照,等待副本期间新上传的对象不在其中。发布通知后会用一次新鲜的 LIST 再检查一遍。这一步不在关键路径上,失败只记日志,由周期巡检兜底。

分叉标记

S3 外部桶的 BlockValidation 对象带一个 is_fork 字段,写节点上传时恒为 false——因为写节点在上传的那一刻还不知道这个块最终是否在规范链上。checker 负责回写。 三条触发路径: pipeline_fork_scan_rewrites_total 不为零说明标记曾被覆盖或此前失败,值得关注。
leafage-evm 在 S3 追赶时依赖这个标记来把高度解析成规范哈希。checker 长时间停摆会让新节点的追赶变慢。

节点状态维护

每轮轮询后,把结果写回 etcd(一个事务提交): 节点离线且没有租约时直接删除该键。高度只在变化时写入,避免无谓的 etcd 写放大。 这两个键是 nodex-proxy 路由的全部依据:节点池成员来自前者,State / Archive 的选择阈值来自后者。

双模式

versionouter_version_new_block_topic 两个配置共同决定。
同时写 version topic 和 singleton topic。
  • etcd 键使用 {chainID}/{version}/ 前缀
  • S3 路径包含 version 段
  • singleton topic 的写入权需要通过 etcd 分布式锁({chainID}/outer_block_notice)选举
  • Leader 定期比对 {chainID}/version,版本不匹配时释放锁
切换版本时,新 Leader 需要把 singleton topic 对齐到自己的进度:向前快进,或回退到共同祖先后重放。这段逻辑在 AlignOuterSingletonalign

本地存储

Pebble DB 保存已确认的区块,双索引:
用于对外提供区块查询,也用于分叉巡检时判断某个高度的规范哈希。

JSON-RPC 接口

监听地址由 listen 配置(默认 :8663),同一端口提供 GET /metrics
错误统一返回 -39005

指标

运行

配置项见配置参考

开发

check/critical_path_test.go 覆盖了关键路径的幂等与重试语义,改动 Process 流程时应先读它。