[经验分享] 优化 snapshot 创建过多现象

背景

在 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, 防止上述的两种情况发生.

11赞

我来提供一种新的实现思路。
对于leader我提供一种新的信息存储对象

haveSendedSnapShot map[uint64]int

这个记录了已发送每一个snapshot对象,以及发送时长。

peer ticktime
1 2
2 3

ticktime 随逻辑时钟周期增长

当有新的snapshot时插入它,ticktime超过pendingSnapShotTimeout或者接受到appendEntriesResponse时, 便可以删除这个记录。

发送snapshot时,就可查这个表。

相对查prs来说能提高速度

1赞