# Lab: Raft - Leader Election

## 说在前面

在实验的过程中，一定要反复阅读：

* Raft Paper Figure 2
* 6.824 Course Notes
* [Students' Guide to Raft](https://thesquareplanet.com/blog/students-guide-to-raft/)
* [Raft Q\&A](https://thesquareplanet.com/blog/raft-qa/)

Leader Election 主要由以下几个部分组成：

* Election Timeout Daemon
* Send RequestVote RPC
* Receive RequestVote RPC
* Marjority Votes Checking Daemon

## Election Timeout Daemon

每个 raft server 都需要一个 daemon 来检查选举超时的情况，当没有收到任何有效消息时，则应该发起一次新的选举；当收到有效消息时，则重置选举超时的计算。这里，有效的消息包括：

* Candidate 发来有效的 RequestVote RPC
* Leader 发来有效的 AppendEntries RPC
* Candidate 收到有效的 RequestVote RPC Reply
* Leader 收到有效的 AppendEntries RPC Reply

因此我们可以在 Make 中创建这样一个 daemon:

```go
go func() {
    for {
        et := generateET()
        select {
        case <-rf.rCh:
        case <-time.After(et): {
            go rf.startsNewElection()
        }
    }
}
```

通过 rCh channel 来接受有效信息的信号。rf.startsNewElection 严格按照 Figure 2 描述的实现即可：

* Increment currentTerm：每个 term 只能有一次选举
* Vote for self：每个 server 在单次选举中只能投一票，参选者默认投给自己
* Reset election timer
* Send RequestVote RPCs to all other servers

## Send RequestVote RPCs

### RequestVoteArgs

RequestVoteArgs 需包含以下字段：

```go
type RequestVoteArgs struct {
    Term         int
    CandidateID  int
    LastLogIndex int
    LastLogTerm  int
}
```

注意这里的 LastLog 指的是 raft server 的 log entries 的最后一个，目的在于让 Follower 判断 Candidate 是否至少和它一样与时俱进。注意不要和 AppendEntriesArgs 中的 PrevLog 混淆，后者指的是 Leader 上次发给 Follower 的最后一个 logEntry，因此 Leader 发给每个 Follower 的 AppendEntriesArgs 都可能不同。

### Retry

由于可能出现网络分区，RequestVote 请求需要超时重发。

## Receive RequestVote RPCs

严格按照 Figure2 描述的步骤实现即可

1. 检查过时请求：term < currentTerm
2. 当 server 还未投出自己宝贵一票，或者已经投了 Candidate 一票时，检查 Candidate 的 log entries 是否与自己与时俱进，如果是，则投它一票。当 server 已经投给 Candidate 一票时，server 的请求响应可能在网络中丢失，因此当再次收到相同请求时，有必要再次同意投票。

## Majority Votes Checking Daemon

这里把检查是否得到多数票的逻辑放到一个 Daemon 里，主要目的在于方便组织代码，将其放在 Send RequestVote RPCs 之后也没有问题：

```go
go func() {
    rf.mu.Lock()
    if rf.isCandidate() && rf.hasMajorityVotes() {
        rf.becomesLeader()
    }
    rf.mu.Unlock()
    time.Sleep(CheckMajorityVoteTimeout)
}
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://zhenghe.gitbook.io/open-courses/mit-6.824/lab-raft-leader-election.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
