多表区块高度对齐订阅
业务背景
在级联实时分析里,下游的目标表是一张 Block 表,按区块粒度写入:每个高度 H 的所有计算结果作为一个批次,原子写入目标表的 H 高度。因此当上游有多张表时,必须先等所有上游表都把 H 处理完毕,才能一次性计算 H、作为一个批次写入目标表;否则 H 批次会缺数据、产生脏批次。 更关键的是,计算函数里常用get_state 读取依赖表的状态快照。读的是哪个高度版本,取决于依赖表的共识高度:只有依赖表的共识高度 ≥ H,get_state 才能返回 H 高度版本的状态数据;否则会因共识高度小于 H 而报错、拿不到 H 的状态。因此必须先等依赖表的水位推进到 H 及以上,才能执行函数。
直接同时订阅多张表的事件流,会因各表计算快慢不一,收到完全乱序的高度通知(例如 trace 已到 18000010,而 token 还在 18000005)。
SDK 内置的 Aligned 对齐状态机,把这两类等待需求区分开,在数据库变更流层面自动完成对齐。
Trigger 表与 Depends 表
Aligned 区分两类表,各自采用不同的对齐规则(类似多流 join,但两类的 join 条件不同):
-
Trigger 表(
loose_align):上游数据源。对齐规则是「所有 Trigger 表都在高度 H 就绪」——因为下游要按 H 批次写入,上游少一张就代表 H 批次缺数据。 -
Depends 表(
strict_align):计算中用get_state读取的状态表。对齐规则是「Depends 表的共识高度 ≥ H」——因为get_state只能读到不超过共识高度的快照,水位没到 H 就读不到 H 的状态。
完整示例
共识高度是怎么计算的
Aligned 通过查询每张 Depends 表的 _height 派生表来计算共识高度:
- 每张 Block 表对应一张
<表名>._height表,每行记录一段已处理的高度区间(block_height_start/block_height_end),写入侧已合并成连续区间。 - 共识高度 = 起始高度最小那行的
block_height_end,即第一个连续区间的右边界。 - 表还没被处理过时,共识高度取
start_height - 1——一张起始高度不为 0 的表,比起点低的高度永远不会有数据。
start_height 起连续提交每个块(含空块),它的共识高度就会持续推进,依赖它的下游才能随之放行。
最佳实践
- Depends 表每个块都要提交任务,包括空块:空块也要提交一个空任务来推进
_height水位,否则 coverage 出现 gap,共识高度就停在 gap 前,下游整条管道会静默停住。 - 单表订阅无需
Aligned:只订阅一张表直接for ... in Subscribe(...).listen()即可。 - 同一张表可同时作为 Trigger 和 Depends:
Aligned会先记它的 ready、再刷新它的水位,保证 diff 区间能看到刚标上的高度。 Subscribe内置重连与游标续传,直接for ... in aligned.listen():消费,无需自己写重试。