线性一致读
raftpp 通过 ReadIndex 路径提供线性一致读支持。该路径不直接返回业务数据,而是为业务层建立一个可以安全读取的时刻。
ReadOnlyOption 定义在 include/raftpp/core/read_only.h 中,提供两种模式:
SafeLeaseBased
通过法定多数确认读许可。该模式更保守,适合默认使用。
LeaseBased
Section titled “LeaseBased”基于 leader lease 判定读许可,延迟更低,但前提更严格。
文档层面必须明确:LeaseBased 依赖 check_quorum = true。如果不满足这一前提,则无法正确建立基于 lease 的读语义。
ReadIndex() 的语义
Section titled “ReadIndex() 的语义”Raftor::ReadIndex() 的成功回调表示:
- RAFT 层已经确认该读取上下文对应的读索引;并且
- 本地状态机应用进度已经达到该读索引。
这一定义来自 ReadyProcessor 的完成逻辑:只有当 applied_index >= read_state.index 时,挂起的读请求才会调用 CompleteRead()。
不直接返回业务数据的原因
Section titled “不直接返回业务数据的原因”ReadIndex() 的职责是建立一致性边界,而不是代替状态机读取业务数据。实际读取通常发生在:
ReadIndex()回调成功之后。- 应用层从状态机或业务存储读取所需值。
在 Raftor 中的处理路径
Section titled “在 Raftor 中的处理路径”Raftor 中的只读请求大致经历以下步骤:
- 业务线程调用
ReadIndex()。 - 请求进入线程安全的
ReadIndexQueue。 - 事件循环线程调用
ProposalTracker::TrackRead()跟踪该请求。 - 事件循环线程调用
raw_node_->ReadIndex(ctx)向 RAFT 层发起读索引请求。 - 当
Ready.read_states返回读索引结果后,ReadyProcessor把它们加入pending_reads_。 - 当本地
applied_index达到对应索引时,触发回调成功。
领导权变化的影响
Section titled “领导权变化的影响”ReadyProcessor 在检测到当前节点失去领导权时,会:
- 将所有挂起提案以
ProposalDropped结束。 - 将所有挂起读请求以
LostLeadership结束。
因此,线性一致读的客户端需要把 LostLeadership 视为正常控制流的一部分。
raftor->ReadIndex("ctx", [](raftpp::Result<void> result) { if (!result) { return; }
// 在这里读取业务状态});- 如果没有明确的延迟优化需求,优先使用
ReadOnlyOption::Safe。 - 如果使用
LeaseBased,必须同时启用check_quorum。 read_index_timeout应与部署环境的网络和调度延迟相匹配。