在做lab3b的时候,测试TestOneSplit时遇到了两个bug,第一个是偶尔会卡住导致超时,一番log和test code查看后,发现是由于5选中leader后被split了,因此出现脑裂的现象,从而导致客户端没法确定哪个是leader而出现消息同步过慢进而超时。
今天为了找第二个bug的解决方案(日志实在看不出了/(ㄒoㄒ)/~~),来到asktug搜索,发现https://asktug.com/t/topic/303079,已经有人写过我第一个bug的解决方案了,在此,我分享一下我的与他大致相同但又更容易实现的方案:
首先需要为leader lease的实现新增两个状态:跟heartbeat一样的作用
// leader lease
leaseElapsed int
leaseTimeout int
然后需要在Progress中增加一个是否收到heartbeat的判断字段:
type Progress struct {
Match, Next uint64
// 用来判断最近是否与progress交互成功
isHeartbeat bool
}
在每次leader收到heartbeatresp和appendresp的时候,需要把对应的progress的isHeartbeat置为true,然后就是需要跟tick heartbeat一样去tick lease,接下来一些细节的实现就ok了。
但是很明显完成这个只能解决我的第一个bug,第二个bug是下面这段测试会过不了(1/30左右的概率):
func TestOneSplit3B(t *testing.T) {
... // 省略
req := NewRequest(left.GetId(), left.GetRegionEpoch(), []*raft_cmdpb.Request{NewGetCfCmd(engine_util.CfDefault, []byte("k2"))})
resp, _ := cluster.CallCommandOnLeader(&req, time.Second)
assert.NotNil(t, resp.GetHeader().GetError()) // 过不了
assert.NotNil(t, resp.GetHeader().GetError().GetKeyNotInRegion()) // 过不了
MustGetEqual(cluster.engines[5], []byte("k100"), []byte("v100"))
}
我已经判断了keyInregion、regionEpoch、regionId,为啥还会出现这种情况呢,有没有相同经历的小伙伴有对应的思路或想法?
更新一下:
最后的解决方法是让leader在收到heartbeat resp和append resp的时候,如果term比自己大也不退位,然后修改一下相关的raft实现就ok了