Table of Contents

产品层 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)——确定性的三原则

  1. 幂等/重放跳过:index <= _appliedThrough 直接返回——快照导入覆盖区间重启重放跳过。
  2. 确定性拒绝 vs 瞬态重试二分(:133-142):StaleDeliveryException/InvalidOperationException (组已存在/epoch 落后——重放必然同判)→ 终局结果;IO/取消等瞬态失败 → 原样上抛交还 apply 管道重试。吞成终局 = 副本状态永久发散(HomeCmd/游标推进丢失且不可恢复)。
  3. 结果表:提案前 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(不重复)——两层

  1. Ack 经共识复制后终局:已确认地址永不重投(apply 侧游标/skip 推进)。
  2. 幂等生产 (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. 相关文档