[经验贴&疑问贴]关于TestOneSplit的经验和疑问

在做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了

关于第一个bug,我之前没碰到过这个问题,我理解

  1. 如果脑裂的话,PD侧会交替更新原先region的信息(一会儿leader是5,一会儿是1-4的leader)
  2. 但当split完成之后,5的RegionHeartBeat就会被一直忽略了(比较RegionEpoch会发现5是stale的)
  3. 而即便split完成之前,CallCommandOnLeader侧也会一直重试,如果当前leader超时,那么其实会尝试random一个别的peer进行Call,这时候再返回NotLeader Error,就能从中知道当前正确的leader是什么(而整个CallCommandOnLeader直到没有结果前,只要不超过5秒或者不够10次都会继续调用,按理说一直找不到leader不太可能?)

关于第二个bug,分裂完成后,我理解这次Request会正常到达leader并且走完Raft Entry的replicate流程,这块儿不知道是不是在Apply Entry的时候没有判断Epoch / Key in region(有时候Propose的时候认为该Entry还是valid的,但是apply过程中,apply它之前更改了RegionEpoch / StartKey和EndKey,就得返回了)(如果这个reply没有error的话,那它会返回值么?能返回的话说明这块儿的逻辑是不是还是有问题,明明在split之后的region上查询一个不在这个region范围内的key,但是还是返回了相应的value)

首先感谢你的回复让我确定了方向,关于第一个bug,是我的表述问题,真正的说法应该是你的回答。第二个我按照指导书已经将各种情况检查了一遍,但还是会出现错误,最后定位应该是创建peer->peer当选的过程过慢从而导致超时,request返回nil,因为test里的超时时间只有1s

此话题已在最后回复的 1 分钟后被自动关闭。不再允许新回复。