Skip to content

提案与错误模型

Raftor 通过 ProposalTrackerProposalQueueReadIndexQueue 管理异步提案与读请求。

普通提案的处理步骤如下:

  1. 调用方调用 Propose()ProposeSync()ProposeAsync()
  2. 请求被放入 ProposalQueue
  3. 事件循环线程生成唯一上下文 node_id:counter
  4. ProposalTracker::Track() 开始跟踪该上下文。
  5. 调用 raw_node_->Propose(ctx, data) 提交日志。
  6. 当对应 entry 被状态机 Apply() 成功应用后,通过 ProposalTracker::Complete() 完成回调。

配置变更提案通过 raw_node_->ProposeConfChange(ctx, cc) 发起。

ReadyProcessor::ApplyEntry() 中,当对应配置变更日志被应用后,会调用:

  • raw_node_.ApplyConfChange(cc)
  • proposal_tracker_.Complete(ctx, "conf change applied")

因此,AddNode() / RemoveNode() 的同步返回只表示“提案已发起”,真正完成点仍是配置变更 entry 被应用。

读请求的处理步骤如下:

  1. 调用方调用 ReadIndex()ReadIndexSync()
  2. 请求被放入 ReadIndexQueue
  3. 事件循环线程调用 ProposalTracker::TrackRead()
  4. 调用 raw_node_->ReadIndex(ctx)
  5. Ready.read_states 返回并且 applied_index >= read_state.index 时,调用 CompleteRead(ctx)

超时由 ProposalTracker::ExpireTimeouts() 统一处理。

超时来源包括:

  • proposal_timeout
  • read_index_timeout
  • 各次调用显式传入的超时时间

超时后,回调收到的错误为 RpcErrorCode::Timeout

Raftor::Stop() 会:

  • 先清空跨线程队列中的未处理请求
  • 再对所有已跟踪的提案调用 FailAll(ShuttingDown)
  • 再对所有已跟踪的读请求调用 FailAllReads(ShuttingDown)

ReadyProcessor::CheckLeadershipChange() 在检测到节点从 leader 退为非 leader 时,会:

  • 对提案调用 FailAll(ProposalDropped)
  • 对读请求调用 FailAllReads(LostLeadership)

节点正在关闭,请求未能完成。

提案在提交或领导权变化过程中被丢弃。

读请求在节点失去领导权时被终止。

请求在限定时间内未完成。

  • 普通提案:对应日志被状态机成功应用,或状态机返回错误后失败完成。
  • 配置变更:对应配置变更日志被应用。
  • 读请求:对应 read index 已被确认,且本地应用进度达到该索引。
  • ProposeSync() / ReadIndexSync() 的超时只表示调用方不再等待,不代表后台状态机一定停止处理。
  • AddNode() 立即把 peer 加入 transport,但不代表成员已经生效。
  • 失去领导权时,未完成提案和未完成读请求的错误语义不同。