本来以为2C很快就结束了,因为在2月29日晚上只是加了读写持久化存储就可以跑过多数测试了,只有2个测试挂掉。为了修复这个问题,开始不断做修改,以至于甚至连最初的选举都影响了,前面的测试各种被break掉,一直只用到atomic包,规避用锁,自己也知道风险,3月开始的前4天非常挫败。3月4号下午开始准备加锁,之后一直修改到3月7号,已经越来越可控,越来越有感觉了。7号晚上可以跑untilfail的测试了,测试40多分钟都没有问题,然后就交到作业提交网站了。

Raft论文Figure 8

月初被block的一个问题是因为Figure 8 unreliable测试一直过不了,直到最后看测试代码,知道模拟一个不可靠网络,有2/3的包延迟会很高,基本上可以认为肯定是超时的。

但开始真正困扰我的,并不是因为网络不可靠问题,而是apply不一致问题。一直没想明白论文中所谓解决apply一致性问题的方法为什么是只要不确认以前期的log就可以,也上网查了,也没找到啥有价值的东西,知道后来还是花了很多时间想清楚,不然根本没法写,有问题也不能知道问题是啥。

TestBackup2B

在测试中,一大目标就是过这个,其他很容易过,就这个难。5号的时候已经可以过这个了,但几乎总在119s左右完成,而测试标准是120s,当时觉得这个太玄,直到7号发现测试选用的超时时间是1s,我用的是论文中的150~300ms的,给选举时间在此基础上扩大100ms,就可以用36s左右过测试了(当然主要是这两天做了很多调整)。

RPC count

不加锁的版本从来没碰到过这类问题,这个问题也就是前两天测试时遇到的,测试要求rpc不能太多,于是调高超时,少发几次,但也经常时50+,要求在60以内,为此还做了一些优化,heartbeat少发(记录上一回AppendEntries的响应时间,只要又不断响应就不发hb),调高的那100ms选举时间也帮助降了rpc数量,如今1s内的数量只有18个左右。

开始尝试调整这些超时参数时,还时不时把Test2B前面的一个测试搞得没法在预定时间内选出新leader,后来发现原因倒不是这个,而是从follower转candidate的条件给写错了,与的条件写成了或,导致大量candidate,term不断加,就是选不出来。

RPC超时

开始一直担心超时问题,但也不知道咋处理超时,看着测试能过就先没弄。后来不行了,给前后时间查打出来,果然看到超过1000ms的,后来看测试代码里写的,确实会有很高的超时。这个资料找了很久,都不是想要的,也看了go uk的一个视频讲context的,还有linode上的一篇文章,最后都是瞎用,也没看明白。后来使用select带time.after的,可以用,但有大量goroutine泄露,然后又去找go的perf工具,用pprof检查了下,也不知道怎么改,虽然用了context,似乎也没把goroutine销毁。这一块到现在也没弄明白,今天给接收结果的channel设成了buffer为1的,另外调高超时参数,让循环少发一些rpc,看样子讲了不少,原来对于高goroutine使用量的Test可以高到上万,现在好像只有几百个。

Data Race

7号忘了测试data race了,今天测试了下,情况比想象的好,主要就是rpc响应的reply在结果为false的情况下仍然被我访问了,而那个地方可能被发rpc call的goroutine写,后面把都加上ok为true时才访问,就消除了。其他倒没什么,就是在VM虚拟机中的ubuntu 18.04测的时候会有

1
2
3
==94244==ERROR: ThreadSanitizer failed to allocate 0x80000 (524288) bytes of clock allocator (error code: 12)
FATAL: ThreadSanitizer CHECK failed: ./gotsan.cc:6976 "((0 && "unable to mmap")) != (0)" (0x0, 0x0)
exit status 66

这个错误在腾讯云的机器上没复现,就没再管。腾迅云上测的结果是goroutine数量大于8192个退出了,然后又去找了go tool trace的东西,生成的网页trace部分,也是最关键的总图,居然渲染不了,看github,有个9天前的反馈,说用gotip可以,gotip是开发版的go,一直go get不了,在sg机器上可以,把gotip的单个文件拷到测试机上,可以用,trace的timeline也总算可以访问了。由此觉得,go的这个东西实在太不成熟了,不可靠,也看到匹配说go的文档很缺,特别是perf这些,深以为然。

弄了半天,也没搞出个所以然,最后还是通过上面说调高超时时间,降低rpc请求频率,另外就是把接受结果的chan改成buffer为1的,总之最后给降下来了。(现在也不记得究竟那个buffer有没有起作用,回头有时间再对比测试一下吧)