它解决什么问题
写节点发布通知的那一刻,副本还没有应用这个区块。外部消费者如果直接订阅内部 topic,就会在 leafage-evm 还查不到该区块时收到通知。 checker 在中间加了一道确认:等到足够比例的副本确实追上了这个高度,才向外部 topic 发布。外部消费者因此得到一个保证——收到通知时,去查询集群一定能读到这个区块。 顺带解决的两件事:分叉块在 S3 上没有标记(谁是规范链只有观察者知道),以及 nodex-proxy 需要知道每个节点当前是否健康。处理流程
check/check.go 的 Process。几个设计要点:
预取与等待并行
预取与等待并行
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 的选择阈值来自后者。
双模式
由version 和 outer_version_new_block_topic 两个配置共同决定。
- 版本模式
- 传统模式
同时写 version topic 和 singleton topic。
- etcd 键使用
{chainID}/{version}/前缀 - S3 路径包含 version 段
- singleton topic 的写入权需要通过 etcd 分布式锁(
{chainID}/outer_block_notice)选举 - Leader 定期比对
{chainID}/version,版本不匹配时释放锁
AlignOuterSingleton 和 align。本地存储
Pebble DB 保存已确认的区块,双索引:JSON-RPC 接口
监听地址由listen 配置(默认 :8663),同一端口提供 GET /metrics。
-39005。
指标
运行
开发
check/critical_path_test.go 覆盖了关键路径的幂等与重试语义,改动 Process 流程时应先读它。