Skip to content

Commit

Permalink
PASSED project2AC
Browse files Browse the repository at this point in the history
  • Loading branch information
Metafora072 committed Jul 11, 2024
1 parent 6ccacbb commit f35e1c7
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 8 deletions.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
49 changes: 49 additions & 0 deletions note/groupmeeting_0712.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
## 本周进度

完成并通过 Project2A

## 难点

1. 理解 Raft 算法。虽然论文中对 Raft 算法的讲述比较详尽,但是涵盖的内容很多,并且中文版本的论文涉及翻译问题,很难一次全部理解。

> 结合动画演示可以很好地帮助理解 Raft 算法。
2. 项目代码框架的理解。涉及的结构体很多,结构体中字段很多,方法很多,初次看到代码非常头疼,几乎不知从何做起。

> 把握三个子任务:领导者选举(Project 2aa)、实现日志复制(Project 2ab)、实现原始节点接口(Project 2ac),先实现 Project 2aa 和 2ab,最后实现 2ac。
>
> ---
>
> 很大的工作量在 Project 2aa 和 2ab。开始实现前,结合论文,搞懂 `RaftLog``Raft``Message``Entry``Progress``Storage`分别是做什么的,里面的字段分别有什么含义,哪些是我们需要用到的。
>
> ---
>
> `MessageType` 将消息划分成了若干类型,理清 `Leader``Candidate``Follower`分别需要处理哪些类型的消息。这又会将任务划分成了若干个子任务,我们可以各个击破,这样实现起来思路就比较清晰了。
3. 代码实现的细节。需要考虑的情况很多,比如 节点投票的条件,节点状态转换的时机,发送Message需要维护的字段,日志条目索引和切片下标的对应,极端情况的处理(比如网络中只有唯一一个节点)等等。

> 结合论文,结合测试信息,多调试。
## 遇到的问题

遇到的问题非常多,debug 花了很长时间,这里列举一些经典的问题。

1. 非Leader节点接收到MessageType_MsgHup消息时,会成为Candidate并发起投票,要考虑到网络中只有自己一个唯一节点的情况。同样地,处理 Leader 追加从上层应用接收到的新日志,并广播给 Follower 时,也要考虑到网络中只有自己一个唯一节点的情况。

> 根据Prs字段的len值获取节点的数量,当这种情况发生时,直接成为 Leader。
2. Candidate收集投票结果时,如果大部分的节点都拒绝,此时应该直接成为 Follower。

> 测试要求的逻辑是这样,可能测试是为了防止这个耗费资源,这个节点已经不可能成为leader了,就别想再尝试当选了
3. 节点同步日志条目时,会发生舍弃自身冲突的日志强行与Leader日志同步的情况,这时候要更新一下 RaftLog 的 stabled 字段的值,因为可能有些日志已经被持久化到 storage 中,但是因为冲突被舍弃掉了。

![](groupmeeting_0712.assets/QQ截图20240712054228.png)

> 日志同步时,将 stabled 字段的值更新为min(历史stabled值,发生冲突的 index 值 - 1)即可
4. 日志索引类型是 uint64 的,raft包中的min函数接受的参数和返回值也是 uint64 的,要注意下溢的发生。

![](groupmeeting_0712.assets/QQ截图20240710070739.png)

> 在上图中,r.RaftLog.LastIndex() 返回的索引值包含0,此时减一会下溢为 INF,在取 min 时就会出错。
Binary file added note/project2A.assets/QQ截图20240710101323.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion note/project2A.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,11 @@ Solution: raft的min函数只能处理uint64类型,Lastindex为0时,减1会发

![](project2A.assets/QQ截图20240710072225.png)

## 相关代码
`handleRequestVoteResponse`中为什么这个需要?

![](project2A.assets/QQ截图20240710101323.png)

## 相关代码

`raft/log.go`**RaftLog** 结构体:

Expand Down
5 changes: 1 addition & 4 deletions raft/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,8 +173,5 @@ func (l *RaftLog) handleCommit(idx uint64,term uint64) bool {
return false
}

// 选举限制
func (l *RaftLog) isUpToDate(index, term uint64) bool {
return term > l.LastTerm() || (term == l.LastTerm() && index >= l.LastIndex())
}


119 changes: 116 additions & 3 deletions raft/rawnode.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,46 +36,70 @@ type SoftState struct {
// Ready encapsulates the entries and messages that are ready to read,
// be saved to stable storage, committed or sent to other peers.
// All fields in Ready are read-only.
// Ready 结构体用于保存已经处于 ready 状态的日志和消息,这些都是准备保存到持久化存储、提交或者发送给其他节点的
// Ready 中所有的字段都是只读的
type Ready struct {
// The current volatile state of a Node.
// SoftState will be nil if there is no update.
// It is not required to consume or store SoftState.
// 表示节点当前的易失性状态,例如其角色(领导者、跟随者、候选人)。这种状态不需要持久化存储,如果没有更新则为 nil。
*SoftState

// The current state of a Node to be saved to stable storage BEFORE
// Messages are sent.
// HardState will be equal to empty state if there is no update.
// 表示节点的持久状态,必须在发送任何消息之前保存到稳定存储中。包括当前任期、投票和提交索引等关键信息。
pb.HardState

// Entries specifies entries to be saved to stable storage BEFORE
// Messages are sent.
// 需要在发送任何消息之前保存到稳定存储中的日志条目列表。
// 需要 持久化
Entries []pb.Entry

// Snapshot specifies the snapshot to be saved to stable storage.
// 指定要保存到稳定存储的快照。
Snapshot pb.Snapshot

// CommittedEntries specifies entries to be committed to a
// store/state-machine. These have previously been committed to stable
// store.
// 需要被输入到状态机中的日志,这些日志之前已经被保存到 Storage 中了
// 需要 apply
CommittedEntries []pb.Entry

// Messages specifies outbound messages to be sent AFTER Entries are
// committed to stable storage.
// If it contains a MessageType_MsgSnapshot message, the application MUST report back to raft
// when the snapshot has been received or has failed by calling ReportSnapshot.
// 如果它包含MessageType_MsgSnapshot消息,则当接收到快照或调用ReportSnapshot失败时,应用程序必须向raft报告。
// 在日志被写入到 Storage 之后需要发送的消息
// 需要 发送
Messages []pb.Message
}

// RawNode is a wrapper of Raft.
// RawNode 是对 Raft 的封装
type RawNode struct {
Raft *Raft
// Your Data Here (2A).
preSoftState *SoftState // 上一阶段的 Soft State
preHardState pb.HardState // 上一阶段的 Hard State
}

// NewRawNode returns a new RawNode given configuration and a list of raft peers.
// NewRawNode 根据配置和节点列表返回一个新的 RawNode
func NewRawNode(config *Config) (*RawNode, error) {
// Your Code Here (2A).
return nil, nil
rawNode := &RawNode{
Raft: newRaft(config),
}
rawNode.preHardState, _, _ = config.Storage.InitialState()
rawNode.preSoftState = &SoftState{
Lead: rawNode.Raft.Lead,
RaftState: rawNode.Raft.State,
}
return rawNode, nil
}

// Tick advances the internal logical clock by a single tick.
Expand All @@ -84,6 +108,7 @@ func (rn *RawNode) Tick() {
}

// Campaign causes this RawNode to transition to candidate state.
// RawNode 转换到候选状态。
func (rn *RawNode) Campaign() error {
return rn.Raft.Step(pb.Message{
MsgType: pb.MessageType_MsgHup,
Expand Down Expand Up @@ -141,21 +166,109 @@ func (rn *RawNode) Step(m pb.Message) error {
}

// Ready returns the current point-in-time state of this RawNode.
// Ready 返回 RawNode 的当前时间点状态。
func (rn *RawNode) Ready() Ready {
// Your Code Here (2A).
return Ready{}
ready := Ready {
Messages: rn.Raft.msgs,
Entries: rn.Raft.RaftLog.unstableEntries(),
CommittedEntries: rn.Raft.RaftLog.nextEnts(),
}

if rn.isSoftStateUpdate() {
ready.SoftState = &SoftState{
Lead: rn.Raft.Lead,
RaftState: rn.Raft.State,
}
}
if rn.isHardStateUpdate() {
ready.HardState = pb.HardState{
Commit: rn.Raft.RaftLog.committed,
Vote: rn.Raft.Vote,
Term: rn.Raft.Term,
}
}

// Snap


return ready
}

// isSoftStateUpdate 检查 SoftState 是否有更新
func (rn *RawNode) isSoftStateUpdate() bool {
return rn.Raft.Lead != rn.preSoftState.Lead || rn.Raft.State != rn.preSoftState.RaftState
}


// isHardStateUpdate 检查 HardState 是否有更新
func (rn *RawNode) isHardStateUpdate() bool {
return rn.Raft.Term != rn.preHardState.Term || rn.Raft.Vote != rn.preHardState.Vote || rn.Raft.RaftLog.committed != rn.preHardState.Commit
}


// HasReady called when RawNode user need to check if any Ready pending.
// 判断是否已经有同步完成并且需要上层处理的信息
func (rn *RawNode) HasReady() bool {
// Your Code Here (2A).
return false
hasReady := false

// 有需要持久化的状态 检查 HardState 是否有更新
if rn.isHardStateUpdate() {
hasReady = true
}

// 检查 SoftState 是否有更新
if rn.isSoftStateUpdate() {
hasReady = true
}

// 有需要持久化的条目
if len(rn.Raft.RaftLog.unstableEntries()) > 0 {
hasReady = true
}
// 有需要应用的条目
if len(rn.Raft.RaftLog.nextEnts()) > 0 {
hasReady = true
}
// 有需要发送的 Message
if len(rn.Raft.msgs) > 0 {
hasReady = true
}

// Snap

return hasReady
}

// Advance notifies the RawNode that the application has applied and saved progress in the
// last Ready results.
// Advance 通知 RawNode,应用程序已经应用并保存了最后一个 Ready 结果中的进度。
func (rn *RawNode) Advance(rd Ready) {
// Your Code Here (2A).

// 上次执行 Ready 更新了 softState
if rd.SoftState != nil {
rn.preSoftState = rd.SoftState
}

// 检查 HardState 是否是默认值,默认值说明没有更新,此时不应该更新 preHardState
if IsEmptyHardState(rd.HardState) == false {
rn.preHardState = rd.HardState
}

// 更新 RaftLog 状态
if len(rd.Entries) > 0 {
rn.Raft.RaftLog.stabled += uint64(len(rd.Entries))
}
if len(rd.CommittedEntries) > 0 {
rn.Raft.RaftLog.applied += uint64(len(rd.CommittedEntries))
}
rn.Raft.msgs = nil
// Snap



}

// GetProgress return the Progress of this node and its peers, if this
Expand Down

0 comments on commit f35e1c7

Please sign in to comment.