MIT 6.5840(6.824) 2023 Spring Lab3 FT-KVServer(3A, 3B) Summary 踩坑记录
前言
时间又过了一个月,这波是终于完成了mit 6.5840(824)的所有lab及challenge了。先上lab3吧。个人认为lab3(写完后)是除了lab1以外最简单的一个,当然还是会踩很多坑。在这篇博客里,我将描述一下lab3的整体框架及各种小坑。那么就开始吧。
整体思路及框架
前置内容
lab3A上来就是要实现一个简单的kvserver。主要包括三种操作:
1. Put(插入)
2. Append(追加)
3. Get(查询)
三种操作的具体区别这里不再赘述了,指导中写得很明白。
lab3A的要求如下:
- 读写均线性化
- 幂等性设计以应付网络错误等
对于lab3B,如果lab2D完成得很好,其实lab3B只需要添加不到20行代码就行了。个人认为特别easy,10分钟搞定,但是却标了个hard标记。可能是认为有些同学需要回头去调lab2D所以标了个hard吧。
相关设计
- lab3A读写线性化设计
- 踩坑记录:
- 在最开始,我没有仔细看指导,因此最开始的设计是保证“读己之写”,也就是类似ZooKeeper的设计。每次服务端的写操作成功后,会返回一个 下标,表示本次操作的日志位置。那么当下一次写操作时,对于同一客户端,需要等待 ,从而保证“读己之写”。 不出所料,寄!
- 然后我尝试使用类似 Quorum 的做法,每次读操作在实现“读己之写”的基础上,同时对 个服务器进行读操作,然后选出其中最新的那个。不出所料,寄!
- 最后,我尝试在每次读操作之前,都会在raft中插入一个NoneOp的日志,用于将之前所有的日志提交,然后再进行读操作。不出所料,寄!
- 正确设计:正如指导中所说的,不仅仅可以将写操作加入raft日志中,将读操作也加入raft日志中就可实现读写线性化(这是因为线性化其实就是需要将操作排个顺序,由于我们在lab2中实现的raft的apply是线性化,是串行的,因此,将所有操作均加入raft日志中,即可实现线性化)
- 踩坑记录:
- lab3A幂等性设计
对于每个 ,分配一个 及 。用 进行 操作的标识。我最开始是在RPC函数中进行幂等性的设计(也就是在 以外),从而防止多余的日志加入到raft中。在lab4中,我还是选择了在 内进行幂等性的设计,《日志任你加,秋后算总账》。同时,最初的设计中, 并不是递增的,而是 交替的(因为我认为 是单线程的,只有当前一个 完成时,才会进入下一个 ),但是测试下来这种做法是错误的,最后还是乖乖改为递增模式了。- 踩坑记录(伪):
- 在最开始,我的幂等性设计与完成提示模块(条件变量 ,将log加入到raft中,会进行对于条件变量 的等待,当 将对应log提交时,会改变条件变量 的状态,并通知在此等待的goroutine)是共享的。对于非leader,应用该log时,会主动创建对应的完成提示模块。这也就是所谓的applier以外的幂等性设计。这种设计实现起来较为复杂,虽然确实可以防止多余的log进入raft,但是代码很丑。虽然整个lab3我都采用了这种方案,并且通过了万次测试,但是在之后的lab4中完全放弃了此种做法,采用了applier内,也就是状态机内进行幂等性设计。
- 正确设计:
- 其实在 内进行幂等性的设计很简单。只需要记录每个 最近应用到状态机的 即可,然后如果之后的 所对应的 那么就认为改操作是重复的。 同时,对于写操作来说,要实现在 内的幂等性设计,需要额外开一个 ,用于存储对应 的 获取结果(因为可能存在应用过慢导致超时,或者网络错误导致客户端没有收到响应,因此客户端会重传)。
- 踩坑记录(伪):
- lab3b相关细节
- 记得将所有状态机相关的内容保存到 中(不仅仅时kv,还有 , 等一切需要在raft内同步的数据)。
- 提交上来的 的 , 那么就直接忽略即可。
部分结构代码
type Op struct {
// Your definitions here.
// Field names must start with capital letters,
// otherwise RPC will break.
Type OpType
Key string
Args string
ClientId ClientId
}
type ClientId struct {
ClerkId int
NextCallIndex int
}
type OpChan struct {
expectTerm int
op Op
cond *utils.MCond // 自己实现的一个可广播的条件变量
opRes OpRes
index int
val string
}
部分逻辑
由于lab3采用的是 外的幂等设计,因此这里就不放了,避免误导大家,lab4的blog中再放吧。
总结
个人认为,在写得过程中,lab3难度小于lab2,稍次于lab4。写完后感觉,lab3难度远小于lab2和lab4。因此可以认为,除了lab1以外,lab3可以算是后3个lab中最简单的了。虽然简单,但是还是踩了很多坑。。甚至lab3虽然过了万次测试,也还在坑里呆着,懒得改了,毕竟也没错。
万次测试结果如图。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通