SubQuery 索引节点(@subql/node)完整演进史:从 0.2.0 到 6.4.6 的核心能力与架构变迁
SubQuery 索引节点(@subql/node)完整演进史:从 0.2.0 到 6.4.6 的核心能力与架构变迁
导读
本篇文章以 packages/node/CHANGELOG.md 为骨架,系统梳理 SubQuery 索引节点(@subql/node)从 2020 年底的 v0.2.0 到 2025 年 11 月的 v6.4.6 之间的全部重大版本演进。你会看到索引节点如何一步步具备批量化拉取、数据库缓存、Worker 多线程、未确认区块、多链重索引、项目热升级、POI 可验证索引、字典(Dictionary)加速等能力,并配合当前仓库源码(packages/node/src)印证这些能力在今天的实际落点,形成一份可直接用于升级选型、排障与二次开发的演进图谱。
一、定位:@subql/node 在 SubQuery 体系中的角色
SubQuery 是一个面向 web3 的通用数据索引框架。@subql/node 是其中负责实际运行索引器、连接链节点、拉取区块、执行 mapping 逻辑并把数据写入数据库的索引节点程序(npm 包名为 @subql/node,CLI 入口为 subql-node,见 packages/node/package.json)。
从源码看,它由 NestJS 组织应用:
- packages/node/src/main.ts 负责解析 yargs 参数并初始化 logger 与 profiler;
- packages/node/src/yargs.ts 将 CLI 参数配置统一委托给
@subql/node-core/yargs,并注册testing、force-clean、reindex三个子命令; - packages/node/src/app.module.ts 组合
DbModule、CoreModule、FetchModule、MetaModule等模块,其中FetchModule(packages/node/src/indexer/fetch.module.ts)是索引核心,注册了ApiService、RuntimeService、IndexerManager、blockDispatcherFactory(Worker 分发器工厂)、SubstrateDictionaryService等关键服务; - packages/node/src/init.ts 展示了启动顺序:先初始化项目(
projectService.init()),拿到 finalized 高度,再初始化 runtime 与 fetch 服务,最后监听端口。
@subql/node 的大量通用逻辑被下沉到 @subql/node-core(workspace 依赖,见 package.json 第 30 行),CHANGELOG 中大量 "Bump/Sync with node-core" 条目正是这一架构演进的结果。
二、版本演进总览:三大阶段
从 CHANGELOG 的时间线可以清晰划分出三个阶段:
| 阶段 | 版本区间 | 时间 | 主题 |
|---|---|---|---|
| 奠基期 | 0.2.0 – 0.35.x | 2020-12 – 2022-05 | 核心索引链路成型:区块/事件/调用 handler、批量化拉取、数据库 schema、字典、POI、MMR |
| 稳定与拆分期 | 1.0.0 – 2.x | 2022-05 – 2023-05 | 1.0.0 大版本发布、node-core 拆分、DB 缓存、测试框架、多端点、未确认区块、workers |
| 能力爆发期 | 3.x – 6.x | 2023-06 – 2025-11 | 多链、项目热升级、历史索引(timestamp/block 双维度)、多字典、CockroachDB、schema 迁移、enum 迁移 |
[Unreleased] 区块保留,说明该项目遵循 Keep a Changelog + Semantic Versioning 的规范(文档头 4-5 行)。
三、奠基期(0.2.0 → 0.35.x):把索引链路打磨成熟
3.1 从零到一:handler 与批量化(0.2.0 – 0.7.0)
- v0.2.0(2020-12-22):支持 block handler,SubQuery 表放入独立 db schema,改用
BigInt替代BN; - v0.3.0:支持
callHandler与eventHandler,三大 handler 体系成型; - v0.4.0:允许在项目中定义起始区块(start block)、支持从 tarball 加载项目、
--local本地模式;[BREAKING] manifest 规范升级以支持自定义类型; - v0.7.0:批量拉取区块(batch fetch blocks),同一区块的所有 handler 执行包进同一个数据库事务——这是索引性能与数据一致性的关键基石。
3.2 沙箱与 API 注入(0.5.0 – 0.15.0)
- v0.5.0 把沙箱(sandbox)从 IndexerManager 中解耦出来;
- v0.6.0 支持向沙箱注入并 patch
api,增加specVersion与success过滤器; - v0.12.0 增加
@index索引字段、jsonField的 jsonb 列、实体关系(v0.11.0)、双 map storage 查询修复; - v0.14.0 改为 pull 而非 subscribe 获取新区块高度,规避订阅卡死导致高度失真的问题;
- v0.15.0 起直到 finalized API 就绪前不拉取区块。
3.3 字典(Dictionary):外部 GraphQL 加速(0.17.0 – 0.22.0)
v0.17.0 是奠基期最重要的性能里程碑:引入外部字典,通过 --network-dictionary=<dictionary_HTTP_url> 或 project.yaml 配置,用外部 GraphQL API 预筛出需要处理的区块,配合 fetch service 的额外区块缓冲区,大幅降低无效区块的处理开销。后续版本持续打磨:
- v0.20.2 修复字典返回批次中丢失 block height 的问题;
- v0.20.3/0.26.0 支持自定义数据源使用字典;
- v1.9.2 字典查询改为事件/交易名大小写不敏感(为兼容 go-dictionary)。
3.4 POI 与 MMR:可验证索引(0.21.0 – 0.23.0)
- v0.21.0:加入 Proof-of-Index 服务(
--proof-of-work,实验性),生成并记录索引工作的哈希; - v0.23.0:加入 MMR(Merkle Mountain Range)服务,生成 POI 证明与 MMR 根,存入基于文件的数据库并同步
_poi表,可通过--mmr-path指定本地路径。
这两个特性在今天的 packages/node-core/src/indexer/poi 与 entities/Poi.entity.ts 中仍然活跃,且从 v6.4.4 修复"数组字段导致 POI 问题"可知它一直处于持续维护状态。
3.5 其他奠基期要点
- v0.16.0/0.16.1:表默认移除
created_at/updated_at,提供--timestamp-field开关(v0.25.1 起默认 false); - v0.19.0:schema 数组对象转 Jsonb;
--help显示完整选项; - v0.25.0:支持枚举类型进 store;新增
force-clean子命令、unsafe标志、--port端口选项(端口被占用时自动寻找可用端口,v0.27.0 起); - v0.26.0:
--scale-batch-size按内存自适应调整拉取批量;store 增加bulkCreate();支持从 JS 加载 chainTypes; - v0.28.0:支持从非文件系统位置运行索引器(重构
SubqueryProject)、动态数据源(dynamic datasources)、基于 BlockNumber 的历史 RPC 方法; - v0.32.0:切换到
vm2沙箱(v1.6.0 后再换 x-vm2,v6.4.3 锁定 VM2 版本避免破坏性变更,见 CHANGELOG 2959 号 issue)。
源码印证:manifest 1.0.0 的解析在 packages/node/src/configure/SubqueryProject.ts,通过 parseSubstrateProjectManifest(raw).asV1_0_0 完成;chainTypes 通过 utils/project.ts 的 getChainTypes 加载,对应 v0.26.0/v0.28.0 的 chainTypes 支持。
四、稳定与拆分期(1.0.0 → 2.x):架构重构与性能工程
4.1 1.0.0 大版本与 node-core 拆分
- v1.0.0(2022-05-11):Major release,奠基期功能全部收敛;
- v1.8.0 增加
bulkUpdate/bulkGet到注入 store; - v1.9.0 引入
@subql/node-core包(#1222),链无关逻辑开始下沉;store.getByField支持offset/limit; - v1.10.0 默认启用历史索引(historical),新增
force-clean与reindex子命令,yargs 文件从 node-core 移回 node。
在今天的仓库中,@subql/node-core 已是独立大包(见 packages/node-core),包含 blockDispatcher、dictionary、POI、storeModelProvider、worker 等全套组件;而 node 包的 subcommands 目录正是 force-clean/reindex/testing 三个子命令的入口。
4.2 数据库缓存(DB Cache):2.0.0 的性能里程碑
v2.0.0 引入完整的 Database cache 体系,至今仍是 node-core 的核心:
- 记录数达到
--store-cache-threshold(默认 1000)时批量 flush 到数据库,减少事务次数; - 通过
--store-get-cache-size(默认 500)决定每个实体在缓存中保留的记录数,读直接命中缓存; --store-cache-async让写 store 与区块处理异步化(默认开启);- v2.9.0 增加
store-cache-upper-limit限制 store cache 最大尺寸。
对应实现见 packages/node-core/src/indexer/storeModelProvider/storeCache.service.ts,其 "flush" 行为在 v5.10.0 还随 node-core 更新了"完全同步时的新 flush 行为"。
4.3 测试框架:subql-node test(2.0.0)
v2.0.0 同时引入测试框架:开发者可按 *.test.ts 命名约定在 src/tests 或 src/test 目录编写测试用例,直接运行 subql-node test 验证过滤条件与 mapping handler,无需真正索引整个项目。源码入口为 packages/node/src/subcommands/testing.init.ts,它实例化 TestingService(testing.service.ts)执行测试;v3.2.0 修复了 testing 命令缺失依赖的问题,v5.11.0 修复"拉取区块失败时测试结果未正确更新"。
4.4 未确认区块与多端点(1.12.0 – 2.x)
- v1.12.0:
--unfinalized-blocks开启未确认区块支持(要求启用历史索引)——注意 v6.0.1 已将其在历史索引开启时默认启用; - v1.15.0:多链索引支持、字典 auth link 集成;
- v1.16.0:
bypassBlocks特性,在project.yaml中声明要跳过的区块数组(v5.2.7 修复大区块区间设置问题); - v1.17.0/v1.18.0:拉取后做 block hash 校验、字典与起始高度校验;
- v1.19.0:支持 CockroachDB 与 SSL 连接选项;
- v1.21.0/1.21.1:多端点支持;
--sponsored-dictionary更名为--dictionary-resolver。
4.5 Worker 多线程(1.6.0 起,实验性→成熟)
- v1.6.0:
--workers=<number>启用 Worker 线程,将区块拉取与处理搬进 worker,性能可提升最多 4 倍,worker 数量上限为 CPU 核数;同版本加入--reindex=<blockNumber>重索引与 block handler 的modulo过滤器; - v3.0.8 修复 store bulk 方法在 worker 下的失败;v3.4.7 修复 workers 下动态数据源初始化失败;v4.0.1 修复
workers=0时改用 block dispatcher 而不是报错退出;v6.0.3 修复 worker 导致性能回退的问题。
今天的 Worker 体系分布在 packages/node/src/indexer/worker 与 packages/node-core/src/indexer/worker,且 fetch.module.ts 通过 blockDispatcherFactory 按配置动态选择普通/Worker 分发器。
五、能力爆发期(3.x → 6.x):多链、热升级与数据一致性
5.1 3.x:字典、POI 与日志打磨
- v3.0.1:项目升级特性(Project upgrades,可在特定高度升级项目)、
skipTransactions与 LightBlock 支持;大量代码迁入 node-core(#1797); - v3.3.0:
dictionary-query-size可配置字典查询区块区间; - v3.2.0:日志支持负过滤器,如
--debug="*,-SQL"排除 SQL 日志; - v3.5.0:node-core 带来 CSV 导出与 schema 迁移支持;
- v3.6.1/v3.9.3:修复 POI 负整数生成问题、去掉 subscription triggers 与 notify 函数;node-core 增加重试逻辑。
5.2 4.x:字典 v2 与多字典切换
- v4.0.0:与 node-core 一起同时支持两个版本字典(v1/v2),且支持多个字典端点,索引器按可用区块自动获取并切换字典(#2257);Polkadot API 升级到 10.12.4;
- v4.6.0:加入 monitor 服务记录区块索引动作,提升 POI 精度并为 Admin API 提供调试信息;
- v4.4.x/v4.5.0:chainTypes 支持 hasher 名称字符串;修复缺失 builtins 模块加载。
字典 v1/v2 实现在 packages/node/src/indexer/dictionary/v1 与 v2;v6.3.4 还修复了"字典 v2 在空过滤条件下使用"的问题。
5.3 5.x:时间戳历史索引与缓存开关
- v5.0.0:BREAKING] 要求索引环境时区设为 UTC(Dockerfile 与 package.json 中同步设置
TZ=utc,见 [package.json 的start/start:prod脚本);SubqueryProject改用 node-core 代码; - v5.1.0:支持网络端点配置与请求头指定;
- v5.3.0:历史索引同时支持按区块高度与按时间戳;新增
--enable-cache标志,允许在 DB 与 cache 之间选择 IO 方式; - v5.5.0:
primary-network-endpoint标志存在时报 "Invalid endpoint" 的问题被修复; - v5.7.2/v5.8.0:实现
BlockchainService匹配新 node-core 架构(#2517);Polkadot API 升到 15。
5.4 6.x:多链重索引、enum 迁移与维护收尾
- v6.0.0:支持多链项目重索引(multi chain project reindex);v6.0.1 默认启用未确认区块;v6.0.2 不在 worker 中注入
MultichainRewindService; - v6.1.0:schema 迁移支持新增 enum 值;
- v6.3.0:升级
@polkadot/api@^16(注意 metadata 存在破坏性变更); - v6.3.2:优化 Docker 镜像体积;v6.3.5 锁定 graphql 相关依赖;
- v6.4.x:修复 skipTransactions + workers 下取区块时间戳报错(#2978)、模型
updateOnDuplicate字段计算错误(#2976)、POI 数组字段问题(#2972)、锁定 VM2 版本避免破坏性变更(#2959)。
六、贯穿始终的工程规范与演进规律
6.1 变更管理规范
文档头明确遵循 Keep a Changelog 格式与 Semantic Versioning,每条变更都带 GitHub issue/PR 编号(如 (#2978)),底部维护了从 [Unreleased] 到 v0.2.0 的完整版本对照链接。这为追溯具体修复动机提供了精确锚点。
6.2 版本节奏规律
- 每周/双周快速迭代:2022 年高峰期几乎每周发版,修复型小版本(如 4.x、5.x 的小数位)占多数;
- 破坏性变更集中在大版本:0.4.0(manifest)、1.0.0、5.0.0(UTC 时区要求)是三次明显的 BREAKING 节点;
- 大量 Bump node-core:说明 node 包作为"薄壳",把链无关逻辑持续交给 node-core 复用,这既降低 node 包自身维护成本,也统一了不同链索引器的行为。
6.3 依赖演进路线(Polkadot API)
| 版本 | Polkadot/api | 备注 |
|---|---|---|
| 0.6.0 | 3.6.3 | 早期 |
| 1.13.0 | 9.7.1 | — |
| 1.21.0 | 10.1.4 | — |
| 3.4.0 | 10.10.1 | — |
| 4.0.0 | 10.12.4 | 字典 v2 同版本引入 |
| 5.8.0 | 15 | — |
| 6.3.0 | ^16 | metadata 破坏性变更 |
当前仓库锁定 @polkadot/api@^16.4.7(见 packages/node/package.json),MetaModule 在启动时会记录 sdk 版本(app.module.ts)。
七、关键能力在当前源码中的落点对照
| CHANGELOG 特性 | 当前源码位置 |
|---|---|
| CLI 参数解析与子命令注册 | packages/node/src/yargs.ts、subcommands |
| 启动编排(project→runtime→fetch→listen) | packages/node/src/init.ts |
| 模块装配(Db/Core/Fetch/Meta) | packages/node/src/app.module.ts |
| 项目解析与 chainTypes 加载 | packages/node/src/configure/SubqueryProject.ts |
| 索引核心与 Worker 分发器工厂 | packages/node/src/indexer/fetch.module.ts |
| 字典 v1/v2 | packages/node/src/indexer/dictionary/v1、v2 |
| Runtime(specVersion 管理) | packages/node/src/indexer/runtime |
| API 连接与多端点 | packages/node/src/indexer/api.service.ts、apiPromise.connection.ts |
| 测试子命令 | packages/node/src/subcommands/testing.init.ts |
八、对开发者的实用指引
- 选版参照:需要多链重索引 → ≥6.0.0;需要时间戳历史索引 → ≥5.3.0;需要多字典自动切换 → ≥4.0.0;需要默认历史索引 → ≥1.10.0;需要 DB 缓存 → ≥2.0.0。
- 升级注意:跨 5.0.0 必须将索引环境时区设为 UTC(镜像与 npm 脚本已默认
TZ=utc);跨 6.3.0 需关注@polkadot/api@16对 metadata 的破坏性变更。 - 排障线索:遇到 worker 下区块时间戳报错可对照 v6.4.6(skipTransactions + workers);遇到 POI 数组字段问题可对照 v6.4.4;遇到 model 更新字段计算异常可对照 v6.4.5 的
updateOnDuplicate修复。 - 本地运行:
subql-node支持test、force-clean、reindex三个子命令;主命令从 main.ts 启动,经 init.ts 完成bootstrap()后监听端口(端口被占用自动换用可用端口)。
结语
@subql/node 的 CHANGELOG 是一份浓缩的 web3 索引器工程演进档案:从单节点顺序拉取,到批量事务、DB 缓存、多线程 Worker、外部字典加速、POI/MMR 可验证索引、多链与多字典,再到历史索引与 schema/enum 迁移——每一步都以 issue 编号背书、以 node-core 复用沉淀。对于希望深入 SubQuery 内部实现或评估索引节点能力的开发者而言,这份 CHANGELOG 与 packages/node/src 源码、packages/node-core 通用内核三者对照阅读,是最完整的学习路径。