背景
在 2c 和 3b 中, 常常看到打印了一堆 "xxx request snapshot"的日志, 甚至会出现 “too many open files” 的错误
这主要有以下几点原因:
- snapshot 创建和传输需要较长时间, 这期间可能会生成很多重复的快照
- 网络分区, leader 无论如何也没办法发送快照给 follower
- …
本篇分享, 就是为了解决和优化这个问题.
解决方案
解决网络分区情况
首先, 针对网络分区的情况, 也即 follower 以经和 leader 较长时间没有通信了, 此时 leader 也就无需再发送 snapshot 给 follower
可以在 raft / progress 中引入一个变量 lastCommunicateTs, 记录最后一次通信的时间
在发送 snapshot 时, 判断 leader 和 follower是否已经长时间没有通信了, 是的话, 可以直接返回, 无需再发送快照:
if !r.checkFollowerActive(to, curMs()) {
return false
}
// Return true if the node is active
func (r *Raft) checkFollowerActive(to uint64, curTs int64) bool {
interval := curTs - r.Prs[to].lastCommunicateTs
return interval < r.leaderLeaseTimeout
}
引入 snapshot 传输状态
针对 snapshot 创建和传输时间过长, 为了避免重复创建 snapshot, 可以为每个 follower progress 引入一个 SnapShotStateType
StateSending表示正在发送中
type SnapShotStateType uint64
const (
StateNormal SnapShotStateType = iota
StateSending
)
同时, 为了避免以下两种情况:
- 快照传输失败
- 传输成功, 但是 follower resp 被 drop 了
还需要为 progress 引入一个 tick 变量, snapTick, 代表自从上一次发送快照后的 tick.
当 snapTick 超过一个时间段, 则主动将 SnapShotStateType 置为 StateNormal, 防止上述的两种情况发生.