内容提要
DrasiWake 将中间桥升级为三节点 Raft 集群,基于 DotNext 实现多数派提交与本地投影,可容忍单节点故障。SonnetDB 降级为物化投影,写操作经 Raft 复制,并保留受理与检查点同命令提交的不变量。无多数派时停摆拒写,通过幂等键保证副作用恰好一次。单节点模式也使用同一状态机,并强化证书、配置指纹与启动校验。
延伸解读
为什么选择嵌入式 Raft 而非外部协调
作者评估了三种方案:外部协调服务(如 etcd/Redis 锁)需自行处理脑裂和租约续期,等于重写半个 Raft;共享分布式数据库会破坏 SonnetDB 本地嵌入式体验和 V1 事务语义;最终选择进程内嵌共识协议,每节点持完整副本,写操作多数派提交后落本地。DotNext.AspNetCore.Cluster 在 .NET 生态中嵌入式 Raft 几乎唯一,且 SlikCache 已有成熟装配模式可参照。
SonnetDB 降级为投影后的数据可靠性
HA 版将 SonnetDB 从权威降级为本地物化投影,真相是 Raft 已提交的命令日志。投影丢失、损坏或落后均可从快照加后续日志重建。快照内容必须包含 outbox 记录、checkpoint 和身份映射等重建业务投影所需的全部信息,不能只存最后应用索引,否则日志压缩后新节点或长期落后节点无法恢复。恢复顺序为先验证快照再应用后续命令,投影未追平前节点标记未就绪。
无多数派时停摆拒写的设计取舍
集群挂掉两个节点时,接收、对账、分发 worker 全部取消,写不进任何东西。作者明确拒绝降级为本地写入继续服务,因为两个分区各自本地写会产生两份自称权威的账本,恢复后无法合并。停摆等人修复是诚实的选择。同时,失去领导权和失去多数派被区分为两种事故:前者可能只是正常换届,后者是集群病了,通过 LeadershipToken 和 ConsensusToken 分开观测和处理。
幂等键保障副作用恰好一次
最危险的窗口是 Leader 发出唤醒且 Gateway 受理后,Accepted+checkpoint 命令尚未复制提交时 Leader 倒下。新 Leader 从已提交状态恢复后看到待派发会重发。幂等键跨节点、跨 Leader、跨崩溃边界稳定,新 Leader 用同一 Idempotency-Key 重发,Gateway 查账发现见过则重放结果,MetaSkill 不会跑第二遍。但作者强调这保证的是副作用恰好生效一次,请求本身是 at-least-once,不是 exactly-once。
Q&A
DrasiWake 的桥接组件为什么要从单点升级为 Raft 集群?
因为 V1 阶段桥接组件是单 Host、非 HA,靠文件锁保证同一时刻只有一个进程写 SonnetDB,一旦挂掉整条“数据变化 → Agent 唤醒”链路就中断,恢复时间不可控。升级为三节点 Raft 集群后,可容忍单节点故障,链路不会因一个节点倒下而断掉。
DrasiWake 的 HA 方案为什么选择 DotNext.AspNetCore.Cluster 而不是 etcd 或共享数据库?
引入 etcd 或 Redis 锁做 leader 选举需要自己处理脑裂窗口、租约续期等,相当于半个 Raft 重写,还多一个运维对象;换共享分布式数据库会丢失 SonnetDB 本地嵌入式的体验,且需重新论证事务语义。DotNext.AspNetCore.Cluster 是 .NET 生态中嵌入式 Raft 的成熟选择,提供 HTTP/2 传输、持久化 WAL、快照、动态成员变更,且与 Generic Host 模型贴合,有 SlikCache 的现成参照。
在 HA 架构中,SonnetDB 的角色发生了什么变化?
V1 中 SonnetDB 是权威存储;HA 版将其降级为每个节点本地的物化投影,真相是 Raft 已提交的命令日志。SonnetDB 丢失、损坏或落后都能从快照加后续日志重建。
DrasiWake 在失去多数派时如何处理写操作?
没有多数派时集群原地停摆,接收、对账、分发的 worker 全部取消,拒绝任何新状态提交。不会降级为 SonnetDB 本地写入继续服务,因为两个分区各自本地写会导致恢复后出现两份都自称权威的账本,造成灾难。
DrasiWake 如何保证副作用恰好生效一次?
通过跨节点、跨 Leader、跨崩溃边界稳定的幂等键。当 Leader 在受理后、提交前倒下,新 Leader 重发时使用相同 Idempotency-Key,Gateway 查账发现已见过则重放结果,MetaSkill 不会跑第二遍。但请求本身是 at-least-once,不是 exactly-once。
DrasiWake 的单节点开发模式为什么也使用同一个 Raft 状态机?
为了避免单节点和集群两条代码路径导致 HA 的 bug 潜伏到生产才暴露。单节点即“一个成员的集群”,每次 dotnet run 都在真实执行命令序列化、状态机应用、快照恢复,开发者无意识地测试生产路径。不配任何 DrasiWake:Cluster 节就是 SingleNode 模式,默认体验与 V1 一致。
DrasiWake 在配置漂移方面做了哪些防护?
集群引导时将功能性配置(绑定注册表、Drasi 身份、OpenClaw target 映射、重试策略等)的规范化指纹持久化为集群元数据。节点启动时指纹不匹配就不许当可服务 Leader;Add 新成员前,Leader 会通过管理 gRPC 探测候选节点的版本和指纹,兼容才提交变更。
DrasiWake 的 Raft 集群在部署时有哪些关键注意事项?
三个经典坑:1) ColdStart 只给字典序最小的种子节点,避免多个空目录节点都冷启动形成互不相识的单节点集群;2) 初始成员列表只在首次引导时用,重启时不能用种子列表覆盖已提交的成员变更,改成员走管理接口;3) Raft 端口和管理端口物理分开,管理面用独立 gRPC、TLS 和 Bearer Token,避免将管理面暴露在与心跳同一网络。