产品层 raft 一致性设计(论文核心保证)
本文回答:产品(TierRaftNode / TierQueueReplica)怎么保证 raft 论文核心性质——选举安全 / 领导权完整 / 日志匹配 / 状态机安全 / 有序提交 / 活性,哪些由引擎承担、哪些由产品层显式补强, 以及产品层一致性语义边界(exactly-once vs at-least-once 的精确划分)。
与引擎架构的关系:底层一致性机制见 raft-architecture.md(论文五性质逐条实现映射); 本文是产品层消费面——一个使用者据此判断"我的队列/节点在什么条件下不丢不重、怎么收敛"。
代码引用为仓库相对路径(Queue/… = src/KernLab.Tier.Products.Net/Queue/…)。
1. 装配结构(谁建了什么)
TierRaftNodeBuilder → TierRaftNode.StartCoreAsync(Node/TierRaftNode.cs:169-219)是唯一装配入口:
TierWal ──▶ TierWalRaftStore(IRaftStore 适配:帧/Term/votedFor/prevLog 断言+截尾)
│
Swarm(多源快照,可选,先于引擎)──▶ SnapshotSwarmSync/Transfer
│
ApplyPipeline(store, 业务状态机) ──▶ RaftStateMachine(引擎)
│ └─ apply.SetConfigCallback(raft.PostConfigChanged) ← 配置条目 = apply 产物
宿主调度循环(快照压缩/发布/反熵/基线上报)
- 存储、apply、共识、快照四层装配次序固定;传输双形态(注入 / 内建 TCP)二选一 fail-fast
(
Node/TierRaftNodeBuilder.cs:194-237)。 - 多组装配:
TierRaftHost一个传输上挂 N 组,每组完整装配链经组作用域视图 (引擎零组感知);GroupReplicaRouter做传输绑定 + 组分发 + 提案转发。 - Witness 轻量形态:无 TierWal/apply/Swarm,高水位断言流(只持久化 (LastIndex,LastTerm)+
(Term,VotedFor)),投票计多数派、永不自荐/晋级(
Node/TierRaftWitnessNode.cs)。
2. 共识-状态机模式(TierQueueReplica 实例)
2.1 命令族(六命令,Queue/QueueReplicaCommand.cs:76-205)
Enqueue(可 delayed/幂等键) / Ack(epoch + 地址批) / Group(Create/Delete/Reset) /
Expire(计数终值+死信终结 / Claim 代次+1) / Home(辖权授予/移交/接管,apply 恒 epoch+1) /
Retention(回收线推进)。线格式 [Tag][CorrId][字段声明序],防御上限对齐引擎在途窗口。
2.2 apply 侧状态机(Queue/QueueStateMachine.cs:115-149)——确定性的三原则
- 幂等/重放跳过:
index <= _appliedThrough直接返回——快照导入覆盖区间重启重放跳过。 - 确定性拒绝 vs 瞬态重试二分(
:133-142):StaleDeliveryException/InvalidOperationException(组已存在/epoch 落后——重放必然同判)→ 终局结果;IO/取消等瞬态失败 → 原样上抛交还 apply 管道重试。吞成终局 = 副本状态永久发散(HomeCmd/游标推进丢失且不可恢复)。 - 结果表:提案前
RegisterWaiter(correlation),apply 内TryRemove+TrySetResult—— 重放条目无 waiter 不存储(零泄漏)。
2.3 确定性核心(TierQueue 显式实现 ITierQueueReplicationPort,TierQueue.cs:780-1056)
契约:全部判定依据取自命令载荷或已复制状态,禁止本地时钟/本地水位参与判定——同一命令 序列在任一副本产生同一状态。实例:
ApplyEnqueueAsync:幂等命中短路返回首次地址不追加;Ring 尾分配串行 ⇒ apply 序即地址序, 全组同地址(消息 ID =LogicalAddress)。ApplyAckAsync:epoch 落后 →StaleDeliveryException(确定性拒绝,全组同判)。ApplyGroupCreateAsync:重复到达幂等 no-op(apply at-least-once 契约)。
2.4 辖权(group home)/epoch fencing——单写者如何保证
- 辖权表是 apply 产物(
QueueStateMachine.cs:34,62-66),随快照持久化;变更经GroupHomeChanged广播(订阅方重连)。 - Dequeue 前
EnsureGroupHome(TierQueueReplica.cs:265-270):非辖权节点抛NotGroupHomeException(携带当前 home,异常驱动客户端重定向)——Dequeue 纯本地读零共识。 - fencing(epoch 递增):任何 HomeCmd / Claim 接管恒
ApplyEpochBumpFromConsensusAsync(epoch+1);Ack 命令携带辖权节点视图 epoch,apply 侧 epoch 落后 → 确定性拒绝—— 旧辖权迟交不可能成功,双辖权投递不可能(验证矩阵 #4/#12 对抗钉)。 - 引导唯一提案方(
TierQueueReplica.cs:113-143):当选 leader 且$default辖权未定 → 延迟 2s 复查(防 replay 滞后误判抢夺已设辖权组)后再自领——leader 恒一 = 恰一提案方。 - 到期记账/retention 由辖权节点后台循环发起(
HomeLoopAsync/SweepExpiredAsync),经 ExpireCmd/RetentionCmd 走共识——回收动作全组一致,禁止本地直改复制状态。
3. 一致性语义:exactly-once 与 at-least-once 的精确边界
3.1 at-least-once(不丢失)
- pending 簿记纯内存(不进 raft,
TierQueueReplica.cs:24注释)——崩溃丢失 = 重放窗口 重投递;缓冲确认(BufferedAcks)也是本地簿记——崩溃 = at-least-once 重投继承。
3.2 exactly-once(不重复)——两层
- Ack 经共识复制后终局:已确认地址永不重投(apply 侧游标/skip 推进)。
- 幂等生产 (ProducerId,Seq):入口快路径
TryFindIdempotent(本地已应用视图,TierQueueReplica.cs:300-314)+ apply 侧终判——重复 Enqueue 返回首次地址不追加。
3.3 提交前不可见 + read-your-writes
- Enqueue 未 applied 不可读(
TierQueueReplica.cs:24)。 - 入口 read-your-writes:提案返回前
WaitForAppliedAsync(index)(本地 apply 水位越过,TierQueueReplica.cs:154-172);follower 转发链路同样等本地水位(:206-210)。
3.4 明确不买
- 复制版不支持 Session 域 2PC(
GetGroupParticipant/AckInRoundAsync→NotSupportedException) ——跨节点恰好一次归规模化候选(TierQueueReplica.cs:634-636,1068-1070)。
4. 论文核心性质映射表
| 论文性质 | 引擎提供(见 raft-architecture.md) | 产品层显式补强 |
|---|---|---|
| 选举安全 | PreVote / IsUpToDate 投票 / 落盘先于应答 / learner-witness 角色门 | leader 恒一 ⇒ 辖权自领的唯一提案方;辖权组不随每次换届强制搬迁 |
| 领导权完整 | 任期锚点 no-op / commit 只经当前任期条目 / ReadIndex 锚点门 | T6 apply 追平守卫:快照压缩前 appliedIndex ≥ persistedIndex 才允许(Node/TierRaftNode.cs:305-318——N₀ 越过 applied 会把待 apply 条目吞进快照区 = 永久停滞);warmup 门:以 raft 自身 CommitIndex(非持久尾)为追平标尺(TierQueueReplicaBuilder.cs:281-299) |
| 日志匹配 | prevLog 断言+截尾 / 已提交区保护 / hint 回退 / 快照衔接 | 日志即状态机:IStateMachine 确定性契约 + QueueStateMachine 幂等跳过/确定性拒绝 |
| 状态机安全 | CommitPair 原子 / apply 单 worker 有序 / at-least-once 重放 | 确定性拒绝 vs 瞬态重试二分;apply 序即地址序;快照导入跳过前缀;epoch/fencing 作命令载荷内确定性判据 |
| 有序提交 | 提交全序 / 批内序 / apply 序 / 乱序防御 | 入口 read-your-writes;WaitForDurableAsync = applied 档(TierQueueReplica.cs:751-755) |
| 活性 | 泵线程 / DeadlineRegistry / 调度饥饿鲁棒 / 复制自愈 | 宿主循环单轮异常吞并续跑(Node/TierRaftNode.cs:295-298);Windows timeBeginPeriod(1) 高精度定时(:241-250);反熵对账周期(仅 leader 发起);成员对账循环(Host/TierRaftHost.cs:206-227) |
5. 稳定与运维机制
- 快照压缩三守卫:增长阈值触发 + T6 apply 追平守卫 +
_followerAppendGate串行 (Node/TierRaftNode.cs:304-347)——快照与 append 不交错。 - 反熵对账(
SwarmAntiEntropy):Merkle 根相等零传输、不等只传差异块——漂移收敛。 - 基线上报:非 leader 向已知 leader 单边声明;换届(
OnLeaderChanged(false))清持有表 + 反熵停发 + 重报(Node/TierRaftNode.cs:258-267,349-362)。 - 退役编排:
DeprovisionAsync(节点下线清理);DisposeAsync分段有界收尾 (宿主循环 → raft → apply → 多源 → TierWal → 传输,每段超时告警不挂死)。 - 故障注入本地形态:时钟缝(假钟快进/墙钟跳变)、传输缝(延迟/分区/丢包/乱序)、引擎缝 ——见 raft-node.md 故障注入一节。
6. 已知边界与开放项
- 复制版无跨节点 2PC(§3.4);跨节点恰好一次归规模化候选。
- 幂等快照窗口 / RetryBackoff 等细节在内部复制规格的已知缺口清单(内部设计稿,不随包发布)。
7. 相关文档
- 引擎架构(论文五性质实现映射):raft-architecture.md
- 使用指南:产品装配 raft-node.md;引擎调用 net.md