TinyKv Project3 PartC Multi-raft KV

2023-05-16

TinyKv Project3 PartC Multi-raft KV

  • 前言
  • Project3 PartC Multi-raft KV 文档翻译
  • processRegionHeartbeat
  • Schedule

前言

  3C要求我们实现调度,3c按照文档直接写即可,比较简单。

在这里插入图片描述

Project3 PartC Multi-raft KV 文档翻译

C部分

  正如我们上面所介绍的,我们的kv存储中的所有数据被分割成几个 region,每个region 都包含多个副本。一个问题出现了:我们应该把每个副本放在哪里?我们怎样才能找到副本的最佳位置?谁来发送以前的 AddPeer 和 RemovePeer 命令?Scheduler承担了这个责任。

  为了做出明智的决定,Scheduler 应该拥有关于整个集群的一些信息。它应该知道每个 region 在哪里。它应该知道它们有多少个 key。它应该知道它们有多大…为了获得相关信息,Scheduler 要求每个 region 定期向 Scheduler 发送一个心跳请求。你可以在 /proto/proto/schedulerpb.proto 中找到心跳请求结构 RegionHeartbeatRequest。在收到心跳后,调度器将更新本地 region 信息。

  同时,调度器会定期检查 region 信息,以发现我们的 TinyKV 集群中是否存在不平衡现象。例如,如果任何 store 包含了太多的 region,region 应该从它那里转移到其他 store 。这些命令将作为相应 region 的心跳请求的响应被接收。

  在这一部分,你将需要为 Scheduler 实现上述两个功能。按照我们的指南和框架,这不会太难。

代码

  需要修改的代码都是关于 scheduler/server/cluster.go 和 scheduler/server/schedulers/balance_region.go 的。如上所述,当调度器收到一个 region 心跳时,它将首先更新其本地 region 信息。然后,它将检查是否有这个 region 的未决命令。如果有,它将作为响应被发送回来。

  你只需要实现 processRegionHeartbeat 函数,其中 Scheduler 更新本地信息;以及平衡- region 调度器的 Scheduler 函数,其中 Scheduler 扫描存储并确定是否存在不平衡以及它应该移动哪个 region。

收集区域心跳

  正如你所看到的,processRegionHeartbeat 函数的唯一参数是一个 regionInfo。它包含了关于这个心跳的发送者 region 的信息。Scheduler 需要做的仅仅是更新本地region 记录。但是,它应该为每次心跳更新这些记录吗?

  肯定不是!有两个原因。有两个原因。一个是当这个 region 没有变化时,更新可能被跳过。更重要的一个原因是,Scheduler 不能相信每一次心跳。特别是说,如果集群在某个部分有分区,一些节点的信息可能是错误的。

  例如,一些 Region 在被分割后会重新启动选举和分割,但另一批孤立的节点仍然通过心跳向 Scheduler 发送过时的信息。所以对于一个 Region 来说,两个节点中的任何一个都可能说自己是领导者,这意味着 Scheduler 不能同时信任它们。

  哪一个更可信呢?Scheduler 应该使用 conf_ver 和版本来确定,即 RegionEpoch。Scheduler 应该首先比较两个节点的 Region 版本的值。如果数值相同,Scheduler 会比较配置变化版本的数值。拥有较大配置变更版本的节点必须拥有较新的信息。

简单地说,你可以按以下方式组织检查程序:

  • 检查本地存储中是否有一个具有相同 Id 的 region。如果有,并且至少有一个心跳的 conf_ver 和版本小于它,那么这个心跳 region 就是过时的。

  • 如果没有,则扫描所有与之重叠的区域。心跳的 conf_ver 和版本应该大于或等于所有的,否则这个 region 是陈旧的。

那么 Scheduler 如何确定是否可以跳过这次更新?我们可以列出一些简单的条件。

  • 如果新的版本或 conf_ver 大于原来的版本,就不能被跳过。

  • 如果领导者改变了,它不能被跳过

  • 如果新的或原来的有挂起的 peer,它不能被跳过。

  • 如果近似大小发生变化,则不能跳过。

  不要担心。你不需要找到一个严格的充分和必要条件。冗余的更新不会影响正确性。

  如果 Scheduler 决定根据这个心跳来更新本地存储,有两件事它应该更新:region tree 和存储状态。你可以使用 RaftCluster.core.PutRegion 来更新 region-tree ,并使用 RaftCluster.core.UpdateStoreStatus 来更新相关存储的状态(如领导者数量、区域数量、待处理的同伴数量…)。

实现 region balance 调度器

  在调度器中可以有许多不同类型的调度器在运行,例如,平衡-区域调度器和平衡-领导调度器。这篇学习材料将集中讨论平衡区域调度器。

  每个调度器都应该实现了 Scheduler 接口,你可以在 /scheduler/server/schedule/scheduler.go 中找到它。调度器将使用 GetMinInterval 的返回值作为默认的时间间隔来定期运行 Schedule 方法。如果它的返回值为空(有几次重试),Scheduler 将使用 GetNextInterval 来增加间隔时间。通过定义 GetNextInterval,你可以定义时间间隔的增加方式。如果它返回一个操作符,Scheduler 将派遣这些操作符作为相关区域的下一次心跳的响应。

  Scheduler 接口的核心部分是 Schedule 方法。这个方法的返回值是操作符,它包含多个步骤,如 AddPeer 和 RemovePeer。例如,MovePeer 可能包含 AddPeer、transferLeader 和 RemovePeer,你在前面的部分已经实现了。以下图中的第一个RaftGroup为例。调度器试图将 peer 从第三个 store 移到第四个 store。首先,它应该为第四个 store 添加 peer。然后它检查第三家是否是领导者,发现不是,所以不需要转移领导者。然后,它删除第三个 store 的 peer。

  你可以使用 scheduler/server/schedule/operator 包中的CreateMovePeerOperator 函数来创建一个 MovePeer 操作。

在这里插入图片描述

在这里插入图片描述

  在这一部分,你需要实现的唯一函数是scheduler/server/schedulers/balance_region.go 中的 Schedule 方法。这个调度器避免了在一个 store 里有太多的 region。首先,Scheduler 将选择所有合适的 store。然后根据它们的 region 大小进行排序。然后,调度器会尝试从 reigon 大小最大的 store 中找到要移动的 region。

  调度器将尝试找到最适合在 store 中移动的 region。首先,它将尝试选择一个挂起的 region,因为挂起可能意味着磁盘过载。如果没有一个挂起的 region,它将尝试找到一个 Follower region。如果它仍然不能挑选出一个 region,它将尝试挑选领导 region。最后,它将挑选出要移动的 region,或者 Scheduler 将尝试下一个 region 大小较小的存储,直到所有的存储都将被尝试。

  在您选择了一个要移动的 region 后,调度器将选择一个 store 作为目标。实际上,调度器将选择 region 大小最小的 store 。然后,调度程序将通过检查原始 store 和目标 store 的 region 大小之间的差异来判断这种移动是否有价值。如果差异足够大,Scheduler 应该在目标 store 上分配一个新的 peer 并创建一个移动 peer 操作。

正如你可能已经注意到的,上面的例程只是一个粗略的过程。还剩下很多问题:

  • 哪些存储空间适合移动?

简而言之,一个合适的 store 应该是 Up 的,而且 down 的时间不能超过集群的MaxStoreDownTime,你可以通过 cluster.GetMaxStoreDownTime() 得到。

  • 如何选择区域?

Scheduler 框架提供了三种方法来获取区域。GetPendingRegionsWithLock, GetFollowersWithLock 和 GetLeadersWithLock。Scheduler 可以从中获取相关region。然后你可以选择一个随机的region。

  • 如何判断这个操作是否有价值?

如果原始 region 和目标 region 的 region 大小差异太小,在我们将 region 从原始 store 移动到目标 store 后,Scheduler 可能希望下次再移动回来。所以我们要确保这个差值必须大于 region 近似大小的2倍,这样才能保证移动后,目标 store 的 region 大小仍然小于原 store。

processRegionHeartbeat

  首先要实现的是一个 processRegionHeartbeat(),用来让集群调度器同步 regions 信息。每个 region 都会周期性的发送心跳给调度器,调度器会检查收到心跳中的region 信息是否合适,如果合适,以此更新自己的记录的 regions 信息。至于怎么检查,怎么更新,官方文档里写的很清晰,Version 和 ConfVer 均最大的,即为最新。

  在 processRegionHeartbeat() 收到汇报来的心跳,先检查一下 RegionEpoch 是否是最新的,如果是新的则调用 c.putRegion() 和 c.updateStoreStatusLocked() 进行更新。

// processRegionHeartbeat updates the region information.
func (c *RaftCluster) processRegionHeartbeat(region *core.RegionInfo) error {
	// Your Code Here (3C).
	epoch := region.GetRegionEpoch()
	if epoch == nil {
		return errors.Errorf("region has no epoch")
	}
	// 1. 检查是否有两个 region 的 id 是一样的
	oldRegion := c.GetRegion(region.GetID())
	if oldRegion != nil {
		oldEpoch := oldRegion.GetRegionEpoch()
		if epoch.ConfVer < oldEpoch.ConfVer || epoch.Version < oldEpoch.Version {
			return errors.Errorf("region is stale")
		}
	} else {
		// 2. 扫描所有重叠的 region
		regions := c.ScanRegions(region.GetStartKey(), region.GetEndKey(), -1)
		for _, r := range regions {
			rEpoch := r.GetRegionEpoch()
			if epoch.ConfVer < rEpoch.ConfVer || epoch.Version < rEpoch.Version {
				return errors.Errorf("region is stale")
			}
		}
	}
	// region 是最新的,更新 region tree 和 store status
	c.putRegion(region)
	for i := range region.GetStoreIds() {
		c.updateStoreStatusLocked(i)
	}
	return nil
}

Schedule

接下来实现 region 调度,该部分用来让集群中的 stores 所负载的 region 趋于平衡,避免一个 store 中含有很多 region 而另一个 store 中含有很少 region 的情况。比如store1想把region1调度到store3上,那么现在store3上增加一个副本,再把原来store1的副本删掉即可

流程官方文档也说的很清楚,大致如下:

  1. 选出 DownTime() < MaxStoreDownTime 的 store 作为 suitableStores,并按照 regionSize 降序排列;
  2. 获取 regionSize 最大的 suitableStore,作为源 store,然后依次调用 GetPendingRegionsWithLock()、GetFollowersWithLock()、GetLeadersWithLock(),如果找到了一个待转移 region,执行下面的步骤,否则尝试下一个 suitableStore;
  3. 判断待转移 region 的 store 数量,如果小于 cluster.GetMaxReplicas(),放弃转移;
  4. 取出 regionSize 最小的 suitableStore 作为目标 store,并且该 store 不能在待转移 region 中,如果在,尝试次小的 suitableStore,以此类推;
  5. 判断两 store 的 regionSize 差别是否过小,如果是小于2*ApproximateSize,放弃转移。因为如果此时接着转移,很有可能过不了久就重新转了回来;
  6. 在目标 store 上创建一个 peer,然后调用 CreateMovePeerOperator 生成转移请求;
// Schedule 避免太多 region 堆积在一个 store
func (s *balanceRegionScheduler) Schedule(cluster opt.Cluster) *operator.Operator {
	// Your Code Here (3C).
	// 1. 选出所有的 suitableStores
	stores := make(storeSlice, 0)
	for _, store := range cluster.GetStores() {
		// 适合被移动的 store 需要满足停机时间不超过 MaxStoreDownTime
		if store.IsUp() && store.DownTime() < cluster.GetMaxStoreDownTime() {
			stores = append(stores, store)
		}
	}
	if len(stores) < 2 {
		return nil
	}
	// 2. 遍历 suitableStores,找到目标 region 和 store
	sort.Sort(stores)
	var fromStore, toStore *core.StoreInfo
	var region *core.RegionInfo
	for i := len(stores) - 1; i >= 0; i-- {
		var regions core.RegionsContainer
		cluster.GetPendingRegionsWithLock(stores[i].GetID(), func(rc core.RegionsContainer) { regions = rc })
		region = regions.RandomRegion(nil, nil)
		if region != nil {
			fromStore = stores[i]
			break
		}
		cluster.GetFollowersWithLock(stores[i].GetID(), func(rc core.RegionsContainer) { regions = rc })
		region = regions.RandomRegion(nil, nil)
		if region != nil {
			fromStore = stores[i]
			break
		}
		cluster.GetLeadersWithLock(stores[i].GetID(), func(rc core.RegionsContainer) { regions = rc })
		region = regions.RandomRegion(nil, nil)
		if region != nil {
			fromStore = stores[i]
			break
		}
	}
	if region == nil {
		return nil
	}
	// 3. 判断目标 region 的 store 数量,如果小于 cluster.GetMaxReplicas 直接放弃本次操作
	storeIds := region.GetStoreIds()
	if len(storeIds) < cluster.GetMaxReplicas() {
		return nil
	}
	// 4. 再次从 suitableStores 里面找到一个目标 store,目标 store 不能在原来的 region 里面
	for i := 0; i < len(stores); i++ {
		if _, ok := storeIds[stores[i].GetID()]; !ok {
			toStore = stores[i]
			break
		}
	}
	if toStore == nil {
		return nil
	}
	// 5. 判断两个 store 的 region size 差值是否小于 2*ApproximateSize,是的话放弃 region 移动
	if fromStore.GetRegionSize()-toStore.GetRegionSize() < region.GetApproximateSize() {
		return nil
	}
	// 6. 创建 CreateMovePeerOperator 操作并返回
	newPeer, _ := cluster.AllocPeer(toStore.GetID())
	desc := fmt.Sprintf("move-from-%d-to-%d", fromStore.GetID(), toStore.GetID())
	op, _ := operator.CreateMovePeerOperator(desc, cluster, region, operator.OpBalance, fromStore.GetID(), toStore.GetID(), newPeer.GetId())
	return op
}

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

TinyKv Project3 PartC Multi-raft KV 的相关文章

  • Multi-Language IDE for Professional Developers (Komodo)

    Komodo is the professional IDE for major web languages including Python PHP Ruby Perl HTML CSS and JavaScript You 39 ll
  • BoT-SORT: Robust Associations Multi-Pedestrian Tracking

    BoT SORT Robust Associations Multi Pedestrian Tracking 论文 xff1a arxiv code github 数据 xff1a 还未找到 文章贡献 运动相机的补偿和卡尔曼状态向量的选择I
  • ovn-central raft HA (by quqi99)

    作者 xff1a 张华 发表于 xff1a 2022 10 12 版权声明 xff1a 可以任意转载 xff0c 转载时请务必以超链接形式标明文章原始出处和作者信息及本版权声明 What s raft RAFT https raft git
  • vscode报错vue/multi-word-component-names处理

    vue multi word component names是用于检测当前的组件名称是否使用驼峰或多单词命名 错误截图如下 xff1a 1 可以修改 eslintrc js文件的配置 module exports 61 root true
  • 【论文复现】AB3DMOT: A Baseline for 3D Multi-Object Tracking and New Evaluation Metrics

    1 前言 AB3MOT是经典的3D多目标跟踪框架 xff0c 将2D卡尔曼推广到了3D 并将2D IOU匹配推广到了3D 由于论文的原理基本上与2D相同所以作者在文中没有叙述很多细节 xff0c 要理解具体实现细节 xff0c 只能看代码
  • MxNet系列——how_to——multi_devices

    博客新址 http blog xuezhisd top 邮箱 xff1a xuezhisd 64 126 com 在多个CPU GPUs上以数据并行方式运行MXNet MXNet 支持在多个CPUs和GPUs上进行训练 其中 xff0c 这
  • WoodScape: A multi-task, multi-camera fisheye dataset for autonomous driving

    Paper name WoodScape A multi task multi camera fisheye dataset for autonomous driving Paper Reading Note URL https opena
  • 二进制哈希码快速搜索:Multi-Index Hashing

    前言 如果你对这篇文章感兴趣 xff0c 可以点击 访客必读 指引页 一文囊括主页内所有高质量博客 xff0c 查看完整博客分类与对应链接 哈希方法通常包含两个部分 xff1a 编码 将元素通过 data dependent 或 data
  • Multi-Object Trackers

    Introduction to Multiple Target Tracking 简单介绍了多目标物跟踪的流程 方法 1 single target tracking STT 单目标跟踪 xff0c 不需要association和assig
  • TinyKv介绍

    TinyKv介绍 前言tinykv架构代码结构如何去写TinyKv参考内容 前言 开一个新坑 xff0c 将tinykv的4个project全部实现 虽然今天我点进去看的时候就萌生退意 好在没有放弃之前 xff0c 把project1完成了
  • TinyKv Project2 PartA RaftKV

    TinyKv Project2a RaftKV 前言Project2 RaftKV 文档翻译Project2A重点内容抛出RaftLogRaftLog结构体字段详解RaftLog核心函数详解 RaftRaft 驱动规则Msg的作用与含义Ms
  • TinyKv Project2 PartB RaftKV

    TinyKv Project2 PartB RaftKV 前言Project2 PartB RaftKV 文档翻译PartB 到底想让我们做什么 xff1f 分析要实现的函数到底要干什么事情proposeRaftCommand 将上层命令打
  • TinyKv Project2 PartC RaftKV

    TinyKv Project2 PartC RaftKV 前言Project2 PartC RaftKV 文档翻译raft节点如何自动的compact压缩自己的entries日志生成快照与快照收收发日志压缩与快照收发总结疑难杂症 前言 pr
  • TinyKv Project3 PartB Multi-raft KV

    TinyKv Project3 PartB Multi raft KV 前言Project3 PartB Multi raft KV 文档翻译发送请求LeaderTransfer 禅让ConfChange 集群成员变更Split regio
  • TinyKv Project4 Transactions

    TinyKv Project4 Transactions 前言Project4 Transactions 文档翻译Project 4 TransactionsTinyKV中的事务Part APart BPart C Percolator x
  • Raft算法的Java实现

    自己这几天在看Redis的Sentinel高可用解决方案 Sentinel选主使用的是著名的Raft一致性算法 本文对Raft的选主作了介绍 具体的算法内容 请参考 Raft 论文 Raft的整体结构 Raft 通过选举一个高贵的领导人 然
  • Raft算法:term什么时候会增加?

    Raft将时间划分为任意长度的术语 如图5所示 术语用连续的整数编号 每个任期以一次选举开始 其中一名或多名候选人试图成为领导者 如第 5 2 节所述 如果候选人赢得选举 那么他将在剩余任期内担任领导者 在某些情况下 选举会导致投票分散 在
  • 在 RAFT 中,是否有可能对某个日志条目达成多数共识,但该条目尚未提交?

    考虑一下官方的这个模拟筏网页 https raft github io Why is term 2 index 1尽管没有承诺S2 leader S3 and S4同意日志吗 我运行了几分钟以确保所有通信均已完成 奇怪的是 如果我再添加一个
  • 网络断开后,raft follower如何重新加入?

    我在木筏上遇到了问题 在论文 寻找一种可理解的共识算法 扩展版 中写道 要开始选举 追随者会增加其当前的 任期并过渡到候选状态 第 5 2 节 它还说 AppendEntries RPC 和 RequestVot RPC 中的接收者应为 R
  • 与恶霸算法相比,高级主选举算法有什么好处?

    我读过当前的主选举算法 如 Raft Paxos 或 Zab 如何在集群上选举主节点 但不明白为什么他们使用复杂的算法而不是简单的恶霸算法 我正在开发一个集群库并使用 UDP 多播来发送心跳消息 每个节点加入一个多播地址 并定期向该地址发送

随机推荐

  • 网络攻防之wireshark抓取登录信息

    使用wireshark抓取登录信息 简介 xff1a Wireshark xff08 前称Ethereal xff09 是一个网络封包分析软件 网络封包分析软件的功能是撷取网络封包 xff0c 并尽可能显示出最为详细的网络封包资料 Wire
  • 头文件互相包含所引发的的问题(深入剖析)

    今天写程序出现了一个让人蛋疼的错误 xff0c 后来发现是由于头文件互相包含所引起的 原本只是简单的以为头文件互相包含只会触发 xff0c 头文件的递归包含 即 xff0c A包含B xff0c 所以才A的头文件里会将B的头文件内容拷贝过来
  • C++11异步操作future和aysnc 、function和bind

    C 43 43 11异步操作future和aysnc function和bind 前言异步操作std future和std aysnc 介绍std future和std aysnc的使用Demostd packaged task 介绍std
  • C++文件服务器项目—FastDFS—1

    C 43 43 文件服务器项目 FastDFS 1 前言1 项目架构2 分布式文件系统2 1 传统文件系统2 2 分布式文件系统 3 FastDFS介绍3 1 fdfs概述3 2 fdfs框架中的三个角色3 3 fdfs三个角色之间的关系3
  • C++文件服务器项目—Redis—2

    C 43 43 文件服务器项目 Redis 2 前言1 数据库类型1 1 基本概念1 2 关系 非关系型数据库搭配使用 2 redis基础知识点2 1 redis安装2 2 redis中的两个角色2 3 redis中数据的组织格式2 4 r
  • C++文件服务器项目—Nginx—3

    C 43 43 文件服务器项目 Nginx 3 前言1 Nginx一些基本概念1 1 Nginx初步认识1 2 正向代理概念理解1 3 反向代理概念理解 2 Nginx的安装与配置2 1 Nginx与相关依赖库的安装2 2 Nginx相关的
  • C++文件服务器项目—FastCGI—4

    C 43 43 文件服务器项目 FastCGI 4 前言1 CGI 概念理解2 FastCGI 概念理解3 FastCGI和spawn fcgi安装4 FastCGI和 Nginx的关系5 Nginx数据转发 修改配置文件6 spawn f
  • C++文件服务器项目—Nginx+FastDFS插件—5

    C 43 43 文件服务器项目 Nginx 43 FastDFS插件 5 前言1 文件上传下载流程1 1 文件上传流程1 2 文件下载流程1 3 文件下载优化流程 2 Nginx和fastDFS插件2 1 安装Nginx和fastdfs n
  • C++文件服务器项目—数据库表设计 与 后端接口设计—6

    C 43 43 文件服务器项目 数据库表的设计 6 前言1 数据库建表1 1 用户信息表 user info1 2 文件信息表 file info1 3 用户文件列表表 user file list1 4 用户文件数量表 user file
  • C语言中宏定义的使用

    1 引言 预处理命令可以改变程序设计环境 提高编程效率 它们并不是 C 语言本身的组成部分 不能直接对 它们进行编译 必须在对程序进行编译之前 先对程序中这些特殊的命令进行 预处理 经过预处理后 程序就不再包括预处理命令了 最后再由编译程序
  • C++文件服务器项目—项目总结与反向代理—7

    C 43 43 文件服务器项目 项目总结与反向代理 7 1 项目总结2 项目提炼3 web服务器的反向代理4 存储节点的反向代理 组件介绍基本写完了 xff0c 后续进行深入 本专栏知识点是通过零声教育的线上课学习 xff0c 进行梳理总结
  • https相关内容

    https相关内容 前言基础概念理解https传输过程 前言 本文写https相关内容 xff0c 持续补充 基础概念理解 对称加密 加解密秘钥是同一个 非对称加密 公钥 私钥 sa gt 公钥私钥都是两个数字ecc gt 椭圆曲线 两个点
  • TinyKv介绍

    TinyKv介绍 前言tinykv架构代码结构如何去写TinyKv参考内容 前言 开一个新坑 xff0c 将tinykv的4个project全部实现 虽然今天我点进去看的时候就萌生退意 好在没有放弃之前 xff0c 把project1完成了
  • TinyKv Project1 Standalone KV

    TinyKv Project1 Standalone KV 前言Project1 StandaloneKV 文档翻译文档的重点内容StandAloneStorageWriteReader Server单元测试 前言 project1还是比较
  • TinyKv Project2 PartA RaftKV

    TinyKv Project2a RaftKV 前言Project2 RaftKV 文档翻译Project2A重点内容抛出RaftLogRaftLog结构体字段详解RaftLog核心函数详解 RaftRaft 驱动规则Msg的作用与含义Ms
  • TinyKv Project2 PartB RaftKV

    TinyKv Project2 PartB RaftKV 前言Project2 PartB RaftKV 文档翻译PartB 到底想让我们做什么 xff1f 分析要实现的函数到底要干什么事情proposeRaftCommand 将上层命令打
  • TinyKv Project2 PartC RaftKV

    TinyKv Project2 PartC RaftKV 前言Project2 PartC RaftKV 文档翻译raft节点如何自动的compact压缩自己的entries日志生成快照与快照收收发日志压缩与快照收发总结疑难杂症 前言 pr
  • TinyKv Project3 PartA Multi-raft KV

    TinyKv Project3 PartA Multi raft KV 前言Project3 PartA Multi raft KV 文档翻译Add RemoveLeaderTransfer 前言 Project3是整个项目最难的部分 xf
  • TinyKv Project3 PartB Multi-raft KV

    TinyKv Project3 PartB Multi raft KV 前言Project3 PartB Multi raft KV 文档翻译发送请求LeaderTransfer 禅让ConfChange 集群成员变更Split regio
  • TinyKv Project3 PartC Multi-raft KV

    TinyKv Project3 PartC Multi raft KV 前言Project3 PartC Multi raft KV 文档翻译processRegionHeartbeatSchedule 前言 3C要求我们实现调度 3c按照