第 21 章 一致性、证明与故障模型
Walrus 应用要面对两类一致性:
- 链上一致性:Sui 对象和事件最终落在 checkpoint 中。
- 数据可用性一致性:Blob 片段被足够多存储节点保存并可读取。
用户看到的“上传成功”只有在这两类状态都达到应用要求后才应该展示。
成功状态的定义
工程上建议把写入状态拆成:
| 状态 | 含义 |
|---|---|
prepared | 本地文件已读取,参数已确认 |
stored | Walrus 数据写入流程完成 |
registered | Sui 交易成功,链上对象可查 |
indexed | 应用索引器已处理事件 |
verified | 后台读取和哈希校验通过 |
前端可以在 registered 后显示“已上传”,但后台仍应推进到 indexed 和 verified。如果产品对可靠性要求高,比如审计日志或数据集登记,应等校验完成后再显示最终成功。
证明和校验
开发者常用三层校验:
- 协议/SDK 校验:确认读取到的是目标
blob_id的内容。 - 业务哈希校验:确认内容等于用户上传或后端生成的原始字节。
- 链上状态校验:确认业务对象引用的 Blob 与当前 owner、版本、epoch 匹配。
不要省掉第三层。否则用户可能拿一个真实存在的 Blob 替换业务对象语义,造成“内容有效但引用非法”。
常见故障
| 故障 | 表现 | 处理 |
|---|---|---|
| publisher 超时 | 上传接口无响应 | 查 Sui 交易和 Blob 状态,避免重复写 |
| aggregator 失败 | 下载 5xx 或超时 | 换 aggregator,重试,检查是否过期 |
| Blob 过期 | 读取失败或状态不可用 | 触发续期或展示已过期 |
| Sui RPC 落后 | 查不到刚提交对象 | 换 RPC 或等待 checkpoint |
| 索引器延迟 | 前端列表没更新 | 直接查 tx/object 兜底,后台补索引 |
| 版本不匹配 | CLI/服务报协议错误 | 升级到目标网络对应 release |
| 网络混用 | testnet Blob 在 mainnet 查询失败 | 所有记录带 network 字段 |
重试策略
重试要按阶段设计:
- 本地编码失败:不重试,提示文件或内存问题。
- HTTP 429:指数退避,尊重服务限流。
- HTTP 5xx:换 endpoint 或短退避重试。
- Sui 交易不确定:先按 digest 查询,不要盲目重发。
- 校验失败:不要返回内容,记录严重告警。
推荐保存一个 upload job:
job_id
file_hash
network
blob_id
blob_object_id
tx_digest
state
retry_count
last_error
这样服务重启后可以继续推进,不会因为内存状态丢失而重复上传。
故障隔离
生产环境至少准备两个读取 endpoint。前端可以配置主 aggregator 和备用 aggregator;后端可以根据健康检查动态选择。
隔离原则:
- 写入服务和读取服务分开扩容。
- 索引器失败不应影响已知 Blob 的直接读取。
- CDN 失败时能回源 aggregator。
- 单个 RPC provider 落后时能切换。
- 后台校验失败不应默默覆盖成功状态。
最终一致的用户体验
最终一致不是让用户猜。界面和 API 应明确状态:
processing:交易或索引还没完成。available:可读取并通过校验。renew_required:快过期。expired:超过存储期。unverified:已上链但后台校验未完成。failed:写入或校验失败。
把这些状态暴露给产品,比把所有异常都显示成“网络错误”更容易运营。