raft论文(四)

Safety

Safety章节主要讨论了,raft如何保证每个节点的状态机,以相同的顺序执行相同的命令。

上面的章节,关于raft的特性,已经讨论了Election SafetyLog MatchingLeader Append-only。接下来,我们首先看看Leader Completeness

Leader Completeness

首先,再次回顾对Leader Completeness的定义:

如果一个日志entry在给定的任期已经被提交,
那么这个日志entry必然会出现在所有任期大于这个给定任期的leaders的日志中

关于Leader Completeness,会有两个约束(规则)

第一个约束

在选举的时候会有一个选举约束

投票者不会给没有自己日志新的Candidate投选票。

Leader在RequestVote时,会携带自己最后一条entry的index和term,投票者会将这两个参数和自己的对比。
关于最新的定义:

1) 最后一个entry的term不同时,term大的为新;
2)最后一个entry的term相同时,index大的为新;

第二个约束

成为Leader后,Commit日志时:

Leader不会Commit当前任期之前任期的日志


(a)阶段 S1 为Leader,复制index = 2, term = 2日志到S2, 其他节点还没有复制成功;
(b)阶段S1发生崩溃,触发Leader选举:
term变成3,S5变成新的Leader。为什么S5可以被推选成新的Leader?
S3、S4可以投票给S5,S1、S2由于在index = 2处的日志比S5新,不会投票给S5,但S5仍然可以有3票当选。
S5当选后,收到来自于客户端的请求,此时会在index = 2, term = 3处追加日志,当时还没有来得及复制日志,发生了崩溃;
(c)由于S5的崩溃,触发选举:
同理term变成4,S1可以获取S2、S3、S4的选票而当选;
S1在index = 3, term=4处追加日志,开始日志复制;
S1将index = 2, term = 2的日志复制到S3,日志条目被复制到大多数的机器上,该日志条目可以被提交(也就是可以被执行),此时S1崩溃;
(d)由于S1的崩溃,触发选举:
term变成5,S5可以通过S2、S3、S4的选票而当选;
S5当选后,会覆盖其他server在index = 2的日志,这样错误就发生了。
为什么说错误发生了?
index = 2, term = 3的日志覆盖了大部分机器上存在的index = 2, term = 2的entry,也就是S1执行过的命令被覆盖。
对于c->d过程中可能产生的错误,raft的做法是:
对于c中index =2, term = 2的日志,因为当前的term = 4, 对于不属于当前term的entry,raft不会根据是否复制到半数以上节点原则来决定是否提交,只有对属于当前term的entry,才会采用这一原则来决定是否提交。
也就是(e)中的,index = 2, term = 2的日志被复制到多数节点后,这个日志还不会提交;只有index = 3, term = 4可以被提交时,index = 2的日志才可以被提交。这样就不会发生S1离线,日志被覆盖的情况。

问题一:

过程(b)中,S2是否会给S5投票?
不会(Election restriction)。
S5的RequestVote RPC为:

	RequestVoteArg {
		Term = 3,
    	CandidateId = me,   // me指自身的节点id
    	LastLogIndex = 1,
    	LastLogTerm = 1,
	}

S2收到S5的请求后,会做如下check:

	//如下是S2最新的日志,这个日志可能是一个committed日志,也可能是一个没有committed的日志
	lastIndex = 2;
	lastTerm = 2;
	// Election restriction
	if arg.LastLogTerm < lastTerm {
		return false;
	}
	if arg.LastLogTerm == lastTerm  && arg.LastLogIndex < lastIndex {
		return false;
	}

基于以上准则,无论S2的第二条日志是否committed,都不会投票给S5

问题二:

index = 3, term = 4可以被提交后,index = 2的日志才可以被提交是什么意思?,index = 2, term = 2是如何被应用的状态机的?

在(e)中S1重新被选举为Leader后:

nextIndex[i] = 3; // 即日志长度的下一个index;

当新的日志条目index = 3, term = 4到达后,开始复制日志,以向S3复制日志为例:

AppendEntryArg {
	term = 4,
	LeaderId = me,
	PrevLogIndex = 2, 
	PrevLogTerm = 2,
	entry = log[3],
}

即会在参数中,携带自身上次同步日志的位置。
在S3侧,自身的日志信息为:

lastIndex = 1;
if lastIndex < arg.PrevLogIndex {
	reply.ConflictIndex = lastIndex + 1;
	return fasle;
}

即会检测到日志的不一致(一致性检查, Log Matching)
S1拿到了日志不一致的问题,会重新Append Entries,且会将从冲突开始的日志,都发送给S3

AppendEntryArg {
	term = 4,
	LeaderId = me,
	PrevLogIndex = 1, 
	PrevLogTerm = 1,
	entry = log[2], log[3],
}

S3收到了新的AppenEntries后,此时一致性检查通过,会append 2条entry
到自己的日志中,且直接更新自己的commitIndex = 3, 此时自己的lastApplied = 1,因此会apply两条entry。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值