TOC
lab1
使用 goland 调试
sequential
distributed
basic
实际上理解了 rpc 后并不困难
基本的流程描述如下
- worker (client) 向 coordinator (server) 请求任务
- coordinator 为 worker 分配任务
- worker 在完成任务后通知 coordinator
coordinator 需要维护任务完成进度的数据结构,并使用 mutex 保护
task
下面进行任务的设计,显然有 map 和 reduce 两个任务
- map
worker 需要得到 filepath / taskno / nReduce
信息
读取输入文件后 map,然后使用 os.CreateTemp
打开一系列的临时文件,通过 ihash(Key) % nReduce
得到输出的中间文件,并通过 json encoder 写入,最后 rename 即可
临时文件默认存放在 /tmp
中,重命名是为了消除随机的后缀,以供 reduce 阶段读取
中间文件的格式为 mr-X-Y
,其中 X 为 taskno (map)
,Y 为 reduce 编号
- reduce
worker 需要得到 taskno / nMap
信息
通过 json decoder 读取所有的中间文件后,sort 然后 reduce,思路类似 mrsequential.go
,最后在工作目录打开输出文件并写入即可
之所以不使用临时文件,是因为后来的实现中避免了对同一文件的读写
所以 map 任务设计中重命名的步骤有点多余
并且硬链接不支持跨文件系统,重命名时会报错 EXDEV
输出文件的格式为 mr-out-X
,其中 X 为 taskno (reduce)
early exit
考虑到 early exit test 中认为,只要 worker 和 coordinator 中有一个 exit,那么整个 Job 就完成了
为此,需要一种机制来同步 worker 和 coordinator 的 exit
方法时引入伪任务 sleep 和 exit
- sleep
出现如下两种情形时,会分配 sleep 伪任务,就是让 worker 睡眠
当所有的 map task 均已分配,但 coordinator 未收到全部的 notification 时
当所有的 reduce task 均已分配,但 coordinator 未收到全部的 notification 时
由于 task 可能超时,所以需要让 worker 睡眠以待命
- exit
出现如下情形时,会分配 exit 伪任务,就是让 worker 退出
当 coordinator 收到全部的 notification 时
另外,即使 coordinator 已经 exit 了,worker 在 rpc 失败后也会自动退出
timeout
下面考虑超时的情形
需要在任务完成进度的数据结构中,维护每个任务开始的时间
当 worker 请求任务时,若 coordinator 发现某个任务在 running 但是已经超时了,就会重新分配该任务给 worker 并重新计时
相应的,worker 在开始任务时,为了避免文件读写冲突,会 remove 所有的相关的输出文件
注意这里细微的语义,参考 https://alfredthiel.gitbook.io/pintosbook/project-description/lab2-user-programs/faq#what-happens-when-an-open-file-is-removed 可知,当 remove 旧输出文件时并创建同名的新输出文件时,即使这两个文件同名,两个 worker 对其的写入也不会冲突
同时 coordinator 在接收 notification 时,也要判断任务是否超时来决定是否更新任务完成进度
log
使用官方的 log 库
worker 中涉及到大量对文件系统的操作,一旦出现错误,就直接 log.Fatalf
退出,根据上述分析,只要 worker 存在,就不会影响整个 Job 的完成,可以参考 crash test
可以重定向至 /dev/null
,以更好的阅读测试输出的结果
test
测试全部通过
由于并发的不确定性,多测几次也可以