在上一篇文章Xline command 去重机制(一)—— RIFL 介绍中,我们从 command 去重机制的契机开始,介绍了去重的必要性以及目前 Xline 的去重机制存在的一些问题,同时讲解了 RIFL(Reusable Infrastructure for Linearizability) 的工作原理,并对其进行了一些性能分析。本文将在此基础上进一步更深讲解。
CURP 中实现 RIFL 存在的问题
RIFL 的目的是作为一个系统的基础设施,提供 RPC 的 exactly-once semantics,自然也可以应用到 CURP 系统中。在 CURP 的 paper 中,多次提到了 RIFL,以及 RIFL 迁移到 CURP 上需要对其进行的一些更改。
这里推荐没有阅读过之前 Xline 源码解析的读者先去阅读并了解一下 CURP 的原理,再继续阅读会更容易理解。
在 CURP paper §C.1 Modifications to RIFL 中,描述了以下两处更改:
首先,由于 witness 的 replay 机制的存在,任何 command 可能会被重复地被新的 master 接受到,如果我们依赖于 RIFL 提供的去重机制,在 replay command 的时候的乱序可能会导致比较靠后的 acknowledgments 拒绝掉了比较靠前的 command 的 replay。例如,恢复出的 Cmd(first_incomplete=5), Cmd(first_incomplete=1),如果按照 5 -> 1 的顺序 replay,那么 1 会被忽略掉。所以在 witness replay 阶段,不进行 processAck。
其次,RIFL 中,server 在 client crash 导致 client_id 过期时会清理这个 client_id 下的所有完成记录,这时在 witness replay 中,过期的 client_id 的 command 会被忽略。所以 client_id 的过期可能需要延迟到 command 同步到 backup server 上之后。
本文的内容是介绍在 Xline 的 CURP 系统中如何实现 RIFL 并解决一些问题。
Xline 中命令去重的具体实现
Lease Server 的实现
在 RIFL 中,LeaseManager 模块将充当一个 client 存活性的见证者,在 Xline 中,去依赖另一套系统提供 Lease 的机制会令人感到困惑,眼下看来,只有将 Lease Server 实现在 Xline 内部才可以解决这个问题。而在三种节点角色中,Leader 是最适合的。
- 实现 Lease Server 之前,我们需要明确需要的功能:
- Client 在发送第一个 proposal 时需要获取到自己的 client id,所以它需要向 Lease Server 注册并获得一个 client id
- Client 需要每隔一段时间向 Lease Server 发送心跳,以确保 lease 租期长期有效
- Client 在自己的 client id 失效时,需要从 Lease Server 处重新获取一个新的 client id
- Leader 在检查一个 proposal 是否过期时,需要检查 Lease Server 中该 client id 是否过期
- Lease Server 应当在 client id 失效时,将其删除回收
确定了上诉的功能之后,可以得到 Lease Server 的 RPC 定义:
message ClientLeaseKeepAliveRequest {
// The optional client_id, 0 means a grant request
uint64 client_id = 1;
}
message ClientLeaseKeepAliveResponse {
// The refreshed(generated) client_id
uint64 client_id = 1;
}
service Protocol {
...
rpc ClientLeaseKeepAlive (stream ClientLeaseKeepAliveRequest)
returns