Contents

MIT 6.824 Lab2C+D笔记

2C——状态持久化(Persistence)

lab 2C是lab 2四个子实验中最简单的了,就是需要学生把论文中提到的需要持久化的状态保存起来,在raft节点宕机恢复之后可以读取,具体来说,需要持久化的字段有:

  • log[] 日志记录
  • currentTerm 当前任期
  • votedFor 当前任期选举时投票给了谁

而leader的nextIndex[]以及matchIndex则可以通过恢复后想follower节点发送AppendEntriesRPC来恢复。然后在做2D日志快照的时候,还有新的字段需要被持久化,接下来就说一下2D——日志快照。(2C确实简单)

2D——日志快照(Snapshot)

lab 2D需要学生实现论文中提到的快照功能:由于在长时间运行raft后,raft的log可能会变得非常长而效率逐渐下滑,且follower节点假如落后很多,将需要很长时间来恢复日志,最后整个集群就会慢到不可用的地步。日志快照就是使用raft的应用对自身的当前状态进行一个快照,然后通知raft可以丢弃快照前的所有log,以此来定期缩短日志,来保持日志的长度在可用范围内。

实际上,实现快照功能需要重写很多逻辑,这次lab也是lab2中花费我最多时间的,后面发现2020年robert morris授课的那学期根本就没有2D,实际上是lab3的内容😂

比较重要的改动是:

  • 2C持久化需要把快照中最后一个日志的任期和index也存起来
  • 假如follower太落后以至其nextIndex[server]小于leader快照后的第一个index,也就是leader已经没有它需要的log了,此时需要我们实现一个InstallSnapShotRPC,把整个snapshot发给follower,直接更新到可以发log的地方。

具体代码就不展示了,写到后面还是用了好多锁,总感觉不够优雅,中途常常遇到各种race的问题,比如install snapshot的时候又接受append entry,或者同时又apply导致各种奇奇怪怪的现象,最后恍然大悟把install snapshot和apply串行化,问题一下子就少了好多

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
func (rf *Raft) applyLog() {
	timer := time.NewTicker(10 * time.Millisecond)
	defer timer.Stop()
	for !rf.killed() {
		select {
		case <-timer.C:
			rf.doApply()
		case <-rf.applyNotify:
			rf.doApply()
		// installSnapshotRPC不能跟背景apply一起跑
		case msg := <-rf.snapCh:
			rf.doSnapshot(msg)
		}
	}
}

lab2测试结果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
midknight@DESKTOP-9G893VB:~/6.5840/src/raft$ time go test -race
Test (2A): initial election ...
  ... Passed --   3.6  3   62   17900    0
Test (2A): election after network failure ...
  ... Passed --   5.5  3  402   80798    0
Test (2A): multiple elections ...
  ... Passed --   5.6  7 1271  261223    0
Test (2B): basic agreement ...
  ... Passed --   0.9  3   18    4898    3
Test (2B): RPC byte count ...
  ... Passed --   1.9  3   48  114584   11
Test (2B): test progressive failure of followers ...
  ... Passed --   4.9  3  266   60216    3
Test (2B): test failure of leaders ...
  ... Passed --   5.4  3  531  117466    3
Test (2B): agreement after follower reconnects ...
  ... Passed --   5.9  3  164   43728    7
Test (2B): no agreement if too many followers disconnect ...
  ... Passed --   3.8  5  406   91150    3
Test (2B): concurrent Start()s ...
  ... Passed --   1.1  3   18    4898    6
Test (2B): rejoin of partitioned leader ...
  ... Passed --   5.4  3  570  131751    4
Test (2B): leader backs up quickly over incorrect follower logs ...
  ... Passed --  18.5  5 3061 2601824  102
Test (2B): RPC counts aren't too high ...
  ... Passed --   2.2  3   34   10178   12
Test (2C): basic persistence ...
  ... Passed --   5.0  3  199   45967    6
Test (2C): more persistence ...
  ... Passed --  17.1  5 2315  511231   16
Test (2C): partitioned leader and one follower crash, leader restarts ...
  ... Passed --   2.2  3   54   13771    4
Test (2C): Figure 8 ...
  ... Passed --  31.8  5 1600  340106   29
Test (2C): unreliable agreement ...
  ... Passed --   2.1  5  426  158046  246
Test (2C): Figure 8 (unreliable) ...
  ... Passed --  35.3  5 9070 19474489   84
Test (2C): churn ...
  ... Passed --  16.6  5 4974 9807901 1343
Test (2C): unreliable churn ...
  ... Passed --  16.6  5 3642 3985790  776
Test (2D): snapshots basic ...
  ... Passed --   5.0  3  138   50814  202
Test (2D): install snapshots (disconnect) ...
  ... Passed --  46.1  3 2399 1524295  356
Test (2D): install snapshots (disconnect+unreliable) ...
  ... Passed --  54.3  3 3084 1604349  322
Test (2D): install snapshots (crash) ...
  ... Passed --  35.3  3 1202 1008637  321
Test (2D): install snapshots (unreliable+crash) ...
  ... Passed --  41.9  3 1723 1352002  316
Test (2D): crash and restart all servers ...
  ... Passed --   8.3  3  184   55156   40
Test (2D): snapshot initialization after crash ...
  ... Passed --   3.4  3   68   20042   14
PASS
ok      6.5840/raft     385.480s

real    6m27.107s
user    1m6.255s
sys     0m10.445s

在2023 lab网页上,提到了lab2所有测试加起来的大概总耗时:

Hint: A reasonable amount of time to consume for the full set of Lab 2 tests (2A+2B+2C+2D) without -race is 6 minutes of real time and one minute of CPU time. When running with -race, it is about 10 minutes of real time and two minutes of CPU time.

在我的实现中,带race和不带race的耗时跟上面的测试结果时间差别不大,6分钟左右+1分钟用户时间,估计新版本的go中race detector做了优化。