project2ab选举的疑问

选举失败为什么不保持在Candidate状态?

// TestLeaderElectionOverwriteNewerLogs tests a scenario in which a

// newly-elected leader does not have the newest (i.e. highest term)

// log entries, and must overwrite higher-term log entries with

// lower-term ones.

func TestLeaderElectionOverwriteNewerLogs2AB(t *testing.T) {

cfg := func(c *Config) {

    c.peers = idsBySize(5)

}

// This network represents the results of the following sequence of

// events:

// - Node 1 won the election in term 1.

// - Node 1 replicated a log entry to node 2 but died before sending

//   it to other nodes.

// - Node 3 won the second election in term 2.

// - Node 3 wrote an entry to its logs but died without sending it

//   to any other nodes.

//

// At this point, nodes 1, 2, and 3 all have uncommitted entries in

// their logs and could win an election at term 3. The winner's log

// entry overwrites the losers'. (TestLeaderSyncFollowerLog tests

// the case where older log entries are overwritten, so this test

// focuses on the case where the newer entries are lost).

n := newNetworkWithConfig(cfg,

    entsWithConfig(cfg, 1),     // Node 1: Won first election

    entsWithConfig(cfg, 1),     // Node 2: Got logs from node 1

    entsWithConfig(cfg, 2),     // Node 3: Won second election

    votedWithConfig(cfg, 3, 2), // Node 4: Voted but didn't get logs

    votedWithConfig(cfg, 3, 2)) // Node 5: Voted but didn't get logs

// Node 1 campaigns. The election fails because a quorum of nodes

// know about the election that already happened at term 2. Node 1's

// term is pushed ahead to 2.

n.send(pb.Message{From: 1, To: 1, MsgType: pb.MessageType_MsgHup})

sm1 := n.peers[1].(*Raft)

if sm1.State != StateFollower {

    t.Errorf("state = %s, want StateFollower", sm1.State)

}

if sm1.Term != 2 {

    t.Errorf("term = %d, want 2", sm1.Term)

}

// Node 1 campaigns again with a higher term. This time it succeeds.

n.send(pb.Message{From: 1, To: 1, MsgType: pb.MessageType_MsgHup})

if sm1.State != StateLeader {

    t.Errorf("state = %s, want StateLeader", sm1.State)

}

if sm1.Term != 3 {

    t.Errorf("term = %d, want 3", sm1.Term)

}

// Now all nodes agree on a log entry with term 1 at index 1 (and

// term 3 at index 2).

for i := range n.peers {

    sm := n.peers[i].(*Raft)

    entries := sm.RaftLog.entries

    if len(entries) != 2 {

        t.Fatalf("node %d: len(entries) == %d, want 2", i, len(entries))

    }

    if entries[0].Term != 1 {

        t.Errorf("node %d: term at index 1 == %d, want 1", i, entries[0].Term)

    }

    if entries[1].Term != 3 {

        t.Errorf("node %d: term at index 2 == %d, want 3", i, entries[1].Term)

    }

}

}

我也感觉需要保持在Candidate的状态。

通过多数节点的拒绝票来转为Follower并不符合Raft的逻辑。

但其实只是节点的状态不一样,他们再次转为Candidate都需要一个随机的选举超时时间,不影响整体的正确性。

另外,etcd中好像也没有这样来实现。。。。。

或许可以问问pingcap tinykv的人,可不可以改一下这个实现逻辑?

好的,谢谢。

多数节点拒绝表示无法选举成功,保持在Candidate状态也没有意义。立即转化成follower应该可以理解成一种加速?

这个保持在Candidate状态是按照Raft论文来的。从Candidate状态转为Follower的条件是discover current leader or new term,并没有多数节点拒绝这一说法。

另外,Candidate状态说明了它一段时间内没有收到来自Leader的消息,当前Leader可能出现了问题,即使这次选举没有成功,那么维持在Candidate状态也是没有问题的。

关于加速这点我不太明白,请问哪里体现出来加速了呢?我是觉得,无论是哪种状态,到下次选举都差了一个随机的选举超时,“速度”是一样的。

Candidate状态说明了它一段时间内没有收到来自Leader的消息,当前Leader可能出现了问题,即使这次选举没有成功,那么维持在Candidate状态也是没有问题的。

回复:因为大多数节点拒绝了它,所以维持在Candidate状态也没有意义,它不会成为下一个leader。

关于加速这点我不太明白,请问哪里体现出来加速了呢?我是觉得,无论是哪种状态,到下次选举都差了一个随机的选举超时,“速度”是一样的。
回复:变成follower之后,会延迟它发起下一轮选举投票的时间。否则它超时之后又会发起新的一轮投票,这是无用的通信。

因为大多数节点拒绝了它,所以维持在Candidate状态也没有意义,它不会成为下一个leader。

回复:多数节点拒绝的情况有很多,它有可能成为下次的Leader。

另外,Candidate下一次选举的时候,term++。

多数节点拒绝的情况有很多,它有可能成为下次的Leader。
回复:譬如?

假设我们当前有5节点,所有节点都是起始Follower状态,日志一样新。
S1 term = 1, Vote = 1
S2 term = 1, Vote = 2
S3 term = 1, Vote = 3
S4 term = 1, Vote = 2
S5 term = 1, Vote = 3
这是一个选票被瓜分的情况。假设S2收到了多数节点的拒绝,它转为Follower,到下次选举差了一个随机的超时时间。这和维持当前的Candidate状态,隔一个选举超时,term++之后再次发起选举不是一样的吗?

1 个赞

懂了,之前没考虑这种情况。。。

becomeFollower会重置选举超时,假设第一次选举开始到收到多数拒绝的时间为t,那么两次选举的间隔会变成randomizedElectionTimeout + t,这反而会延迟选举出新的Leader。

确实如此。但不太确定这个 t 的时间,因此是否是延迟我也不太好说~