【ETCD】[源码阅读]深度解析 raft的 appendEntry方法

java 复制代码
func (r *raft) appendEntry(es ...pb.Entry) (accepted bool) {
	li := r.raftLog.lastIndex()
	for i := range es {
		es[i].Term = r.Term
		es[i].Index = li + 1 + uint64(i)
	}
	// Track the size of this uncommitted proposal.
	if !r.increaseUncommittedSize(es) {
		r.logger.Debugf(
			"%x appending new entries to log would exceed uncommitted entry size limit; dropping proposal",
			r.id,
		)
		// Drop the proposal.
		return false
	}
	// use latest "last" index after truncate/append
	li = r.raftLog.append(es...)
	r.prs.Progress[r.id].MaybeUpdate(li)
	// Regardless of maybeCommit's return, our caller will call bcastAppend.
	r.maybeCommit()
	return true
}

源码分析

1. 获取当前日志的最后索引
go 复制代码
li := r.raftLog.lastIndex()
  • 作用:获取当前日志的最后一个索引号,用于计算新日志条目的索引。
  • 背景:Raft 协议中的日志条目是按顺序存储的,每个条目都有唯一的索引号,索引号从 1 开始递增。
2. 为每个新日志条目赋值
go 复制代码
for i := range es {
    es[i].Term = r.Term
    es[i].Index = li + 1 + uint64(i)
}
  • 作用
    1. 将当前任期号(r.Term)赋值给每个日志条目。
    2. 为每个日志条目计算正确的索引号,li + 1 + uint64(i)
  • 背景:Raft 协议要求每个日志条目必须有明确的任期号和索引号,任期号标识领导者的任期,索引号标识日志在整个日志序列中的位置。
3. 检查未提交日志的大小是否超限
go 复制代码
if !r.increaseUncommittedSize(es) {
    r.logger.Debugf(
        "%x appending new entries to log would exceed uncommitted entry size limit; dropping proposal",
        r.id,
    )
    // Drop the proposal.
    return false
}
  • 作用
    1. 调用 increaseUncommittedSize 方法,计算添加这些日志条目后,未提交日志的总大小是否超过限制。
    2. 如果超限,拒绝提议并返回 false
  • 背景
    • 未提交日志是指已经记录在领导者日志中但尚未被大多数节点确认的日志条目。
    • 为了避免无限制的日志增长导致内存溢出或性能下降,Raft 实现通常对未提交日志的总大小进行限制。
4. 追加日志条目
go 复制代码
li = r.raftLog.append(es...)
  • 作用
    1. 调用 r.raftLog.append 方法,将新的日志条目追加到领导者的日志中。
    2. 返回追加完成后的最后一个日志索引号。
  • 背景:Raft 协议要求领导者将客户端的提议日志条目追加到自己的日志中,并通过心跳或消息传播给其他节点。
5. 更新自身的日志复制进度
go 复制代码
r.prs.Progress[r.id].MaybeUpdate(li)
  • 作用
    1. 调用 MaybeUpdate 方法,更新领导者在 ProgressTracker 中自身的日志复制状态。
    2. 记录当前日志的最新索引号。
  • 背景ProgressTracker 是 Raft 实现中用来跟踪每个节点日志复制进度的核心结构。
6. 尝试提交日志
go 复制代码
r.maybeCommit()
  • 作用
    1. 检查是否可以推进已提交日志的索引号。
    2. Raft 协议规定,日志条目在被大多数节点复制后才能被认为是已提交的(committed)。
  • 背景
    • 已提交的日志条目可以被状态机应用。
    • maybeCommit 方法通过检查日志复制的状态来决定是否推进提交索引。
7. 返回成功
go 复制代码
return true
  • 作用:表示日志条目成功追加到领导者的日志中,并已触发相关操作(更新进度、尝试提交)。

总结

总的来说,该函数实现的代码如下:

  1. 日志条目的赋值与追加:为每个日志条目赋予正确的任期号和索引号,并将其追加到本地日志。
  2. 未提交日志大小限制:通过限制未提交日志的大小,防止系统内存溢出。
  3. 日志复制进度更新 :更新领导者在 ProgressTracker 中自身的日志复制状态。
  4. 推进日志提交:检查是否可以推进已提交日志索引,为状态机应用准备。
  5. 结果反馈 :返回日志条目追加成功与否的结果。
    r.raftLog.append(es...)后会使得n.rn.HasReady()能够返回true。
    这段代码是 Raft 协议中领导者日志管理的关键部分,它确保日志条目在本地日志中的正确性和有效性,同时通过限制未提交日志的大小来维持系统的稳定性。最终,它为后续的日志复制和提交提供了基础,是 Raft 协议实现中不可或缺的组成部分。
相关推荐
简 洁 冬冬几秒前
限制redis内存
数据库·redis·缓存
9ilk2 分钟前
【MySQL】--- 数据库基础
数据库·mysql
猫猫不是喵喵.5 分钟前
【Redis】一人一单秒杀活动
数据库·redis·缓存
信徒_5 分钟前
Redis 和 Mysql 中的数据一致性问题
数据库·redis·mysql
龙少95431 小时前
【深入理解Java线程池】
java·数据库·算法
很楠不爱1 小时前
MySQL——用户管理
数据库·mysql
莫叫石榴姐1 小时前
SQL进阶技巧:如何根据工业制程参数计算良品率?
数据库·python·sql·机器学习·数据分析·线性回归
黑客老李1 小时前
面试经验分享 | 杭州某安全大厂渗透测试岗二面
大数据·服务器·数据库·经验分享·安全·面试·职场和发展
斗-匕2 小时前
MySQL 索引详解
数据库·mysql
Java Fans2 小时前
构建一个简单的基于 HBase 的搜索引擎
数据库·搜索引擎·hbase