MIT6.824 lab3AB记录
实验目标:基于raft日志复制算法实现的线性一致性kv存储引擎。
线性一致性:
- 所有的读操作都能够读取到最近一次写操作的结果。
- 所有节点(或者进程)在同一时刻,看到的数据都是相同的。
简而言之,线性一致性保证所有的操作在系统中是按照其提交的顺序进行执行的,因此,在任何时刻,对于任意一个节点或进程所提出的读操作,都应该返回最新提交的写操作的结果。
当一个节点或进程发出一个写操作之后,其他节点或进程必须立即且无条件地能够看到这个写操作的结果,以保证线性一致性的实现。
需要注意的是,线性一致性通常会带来更高的延迟和更大的开销,因为系统需要通过各种机制来保证不同节点或进程之间的数据同步。
想法:raft保障的日志都是一致性的,所以如果我们把操作通过raft来传播的话,通过applyChan我们就可以得到2)所有节点(或者进程)在同一时刻,看到的数据都是相同的。然后如果我们两个连续的读要读到最近一次写的内容,如果没有别的client并发操作的话。那么在日志看来就是
前缀日志 + 读操作 + 读操作那么根据raft日志的顺序,我们可以发现所有的读操作都能够读取到最近一次写操作的结果。但是这个东西有一个限制,就是我们对于append和put操作只能执行一次,为啥呢。
有人可能认为,只要写请求是幂等的,那重复执行多次也是可以满足线性一致性的,实际上则不然。考虑这样一个例子:对于一个仅支持 put 和 get 接口的 raftKV 系统,其每个请求都具有幂等性。设 x 的初始值为 0,此时有两个并发客户端,客户端 1 执行 put(x,1),客户端 2 执行 get(x) 再执行 put(x,2),问(客户端 2 读到的值,x 的最终值)是多少。对于线性一致的系统,答案可以是 (0,1),(0,2) 或 (1,2)。然而,如果客户端 1 执行 put 请求时发生了上段描述的情况,然后客户端 2 读到 x 的值为 1 并将 x 置为了 2,最后客户端 1 超时重试且再次将 x 置为 1。对于这种场景,答案是 (1,1),这就违背了线性一致性。归根究底还是由于幂等的 put(x,1) 请求在状态机上执行了两次,有两个 LZ 点。因此,即使写请求的业务语义能够保证幂等,不进行额外的处理让其重复执行多次也会破坏线性一致性。
所以我们要线性一致性的话,我们就是要保障put/append操作可以commit的多次,但是只能应用在kv层一次,所以我们可以对每个客户端记录下每个客户端最后的commitIdx.
lab3
client端
我们要找到server集群的leader,要用一个commitIdx来区分请求,使得请求有一个序列,因为client没有并发,所以这个最大值就是最后一个请求,我们需要把这个对于每个客户端记录一下
func (ck *Clerk) PutAppend(key string, value string, op string) {// You will have to modify this function.args := PutAppendArgs{Key: key,Value: value,Op: op,LeaderId: ck.LeaderId,CommandId_PA: ck.commandId,ClientId: ck.clientId,}DPrintf("Node{%v} start appendput{%v} value{%v} op{%v}", ck.clientId, key, args.Value, op)for {var reply PutAppendReplyif !ck.servers[ck.LeaderId].Call("KVServer.PutAppend", &args, &reply) || reply.Err == ErrWrongLeader || reply.Err == ErrTimeOut {ck.LeaderId = (ck.LeaderId + 1) % int64(len(ck.servers))// log.Println(1)continue}ck.commandId++DPrintf("Node{%v} appendput sucess", ck.clientId)return}
}
server端
因为start在raft底层已经上了锁,足够保障并发安全,不用加kv层的锁,不如可能会死锁。然后raft层存的是log of operations。同时read也要记录,因为我们根据的是日志的顺序来决定执行的顺序的。最后那个delete为了减少内存占用。实际上不用担心ch有东西被delete掉了,因为kv有重试机制,同时更重要的是kv applier chan传递时上了锁,delete也上了锁。同时一个apply协程也保障了顺序性。也只有一个在channel等待,同时我们做了一个buffer的channel,根据前面的描述,buffer的大小不影响正确性,我们可以用,因为最多一个在buffer里(因为我们用的是一个aplier协程),但我们不用buffer的话,可能在start后,得到notify_chan的锁就获得不到,因为我们start比较快就commit阻塞在applier了。
func (kv *KVServer) PutAppend(args *PutAppendArgs, reply *PutAppendReply) {// Your code here.var op_type intif args.Op == "Append" {op_type = APPEND} else {op_type = PUT}make_op := Op{ClientId: args.ClientId,OpType: op_type,Key: args.Key,Value: args.Value,CommandId: args.CommandId_PA,}DPrintf("Node{%v} may be ........", kv.me)kv.mu.Lock()if kv.isdupicate(make_op.ClientId, make_op.CommandId) {lastreply := kv.clientsInformation[make_op.ClientId].Last_repyreply.Err = lastreply.Errkv.mu.Unlock()return}kv.mu.Unlock()DPrintf("Node{%v} stall here", kv.me)index, _, is_leader := kv.rf.Start(make_op)if !is_leader {reply.Err = ErrWrongLeaderreturn}kv.mu.Lock()ch := kv.newChannel(index)kv.mu.Unlock()select {case rpc := <-ch:reply.Err = rpc.Errif rpc.Err == OK {// log.Printf("reply will Node{%v} the return reply optype{%v} commidex{%v}, lastapplied{%v} key{%v} value{%v}", kv.me, make_op.OpType, make_op.CommandId, kv.lastApplied, make_op.Key, make_op.Value)}case <-time.After(500 * time.Millisecond):reply.Err = ErrTimeOut}DPrintf("Node{%v} here will reply{%v}", kv.me, reply)kv.mu.Lock()kv.Delete(index)kv.mu.Unlock()}
snapshot要存的是kv storage和clientInformation。先recover snapshot在kv storage和clientinforamtion ,然后raft重放log,接着作用于clientinformation和storage上,不需要持久化kv_storage和client_information。因为下层的raft的log和snapshot已经保障了,重放时会恢复。
func (kv *KVServer) applier() {for !kv.killed() {DPrintf("Node{%v} here applier", kv.me)select {case message := <-kv.applyCh:{DPrintf("Node{%v} try to applymessage{%v}", kv.me, message)if message.CommandValid {kv.mu.Lock()if message.CommandIndex <= kv.lastApplied {DPrintf("Node{%v} command{%v} is less than kv lastapply{%v}", kv.me, message.CommandIndex, kv.lastApplied)kv.mu.Unlock()continue}kv.lastApplied = message.CommandIndexreply := new(CommandReply)make_op := message.Command.(Op)DPrintf("Node{%v} makeop.........{%v}", kv.me, make_op)if make_op.OpType != GET && kv.isdupicate(make_op.ClientId, make_op.CommandId) {DPrintf("Node{%v} is duplicate", kv.me)reply.Err = kv.clientsInformation[make_op.ClientId].Last_repy.Err} else {reply = kv.applylogtoState(make_op)if make_op.OpType != GET {kv.clientsInformation[make_op.ClientId] = ClientInfo{Last_commandId: make_op.CommandId, Last_repy: *reply}}}// kv.rf.Persist(kv.clientsInformation, kv.lastApplied)current_term, is_Leader := kv.rf.GetState()if is_Leader && message.CommandTerm == current_term {DPrintf("Node{%v} get state", kv.me)notify_chan := kv.newChannel(message.CommandIndex)notify_chan <- reply// if kv.maxraftstate != -1 {// kv.rf.Persist(kv.clientsInformation, kv.storage)// }DPrintf("reply to notify chan{%v}", reply)} else {DPrintf("Node{%v} is not leader", kv.me)}// 3Bif kv.rf.ShouldSnap(kv.maxraftstate, message.SnapshotIndex) {DPrintf("Node{%v} start to snapshot index{%v}", kv.me, message.CommandIndex)kv.snapMake(message.CommandIndex)}kv.mu.Unlock()} else if message.SnapshotValid {kv.mu.Lock()kv.storeSnapshot(message.Snapshot)kv.lastApplied = message.SnapshotIndexkv.mu.Unlock()} else {panic(fmt.Sprintf("Valid message{%v}", message))}}}}
}
相关文章:
MIT6.824 lab3AB记录
实验目标:基于raft日志复制算法实现的线性一致性kv存储引擎。 线性一致性: 所有的读操作都能够读取到最近一次写操作的结果。所有节点(或者进程)在同一时刻,看到的数据都是相同的。 简而言之,线性一致性…...
一分钟了解美国棒球体系·棒球1号位
美国棒球体系是一个庞大且复杂的体系,涵盖了从青少年到职业的各个层次。下面是美国棒球体系的主要组成部分: 1. 青少年棒球(Youth Baseball) 美国的青少年棒球体系包括各种地区和全国性的联盟,如Little League、Pony…...
通过ObjectMapper和JsonNode 把JSON字符串转换成树结构数据和获取树节点数据
一.简介 今天同事有个需求,要把一个JSON字符串转换成一个树结构的数据并获取节点数据,鉴于自己不想写递归去转换,于是使用ObjectMapper和JsonNode类去实现。 二.依赖 pom文件引入依赖: <dependency><groupId>com.…...
鉴源论坛 · 观模丨面向界面的图形化测试技术
作者 | 熊一衡 华东师范大学软件工程学院博士 苏亭 华东师范大学软件工程学院教授 版块 | 鉴源论坛 观模 01 什么是面向界面的图形化测试(GUI Testing) 图形用户界面(GUI) 是一种通过图形化方式呈现信息、数据、功能和操作的用户界面,旨在…...
Midjourney以图生图的详细教程(含6种案例介绍)
🏆 文章目标:学习并介绍Midjourney以图生图的详细教程 🍀 Midjourney以图生图的详细教程 ✅ 创作者:熊猫Jay 🎉 个人主页:Jay的个人主页 🍁 展望:若本篇讲解内容帮助到您,…...
基于单片机的电路特性测试仪的设计
摘 要 当今社会科技的飞速发展,智能和便捷已经成为人们的日常诉求。现在放大电路在使用过程中经常出现故障,并且需要测试电路数据,但是大多数是手动进行测试,一定程度上影响了工作效率。 为了测量数据更安全更便捷,针…...
五一将迎2亿人次出行,君子签助力旅行社合规高效签旅游电子合同
近日,为规范旅游市场秩序,促进旅行社高质量发展,文旅部发布了《文化和旅游部办公厅关于进一步规范旅游市场秩序的通知》(下称《通知》),对旅游业提出了新的要求。 《通知》中规范了旅行社经营行为。旅行社要…...
IAP升级遇到的问题
文章目录 1. app程序在SystemClock_Config中跑飞2. 程序HAL_Delay中卡死3. 通过外部flash模拟的U盘没能被电脑识别4. 将bin文件拷贝到片内flash中失败5、APP程序跳转过后串口不能工作 这几天在STM32G473使用IAP升级的时候踩了不少坑 1. app程序在SystemClock_Config中跑飞 boo…...
简单聊聊k8s,和docker之间的关系
前言 随着云原生和微服务架构的快速发展,Kubernetes和Docker已经成为了两个重要的技术。但是有小伙伴通常对这两个技术的关系产生疑惑: 既然有了docker,为什么又出来一个k8s? 它俩之间是竞品的关系吗? 傻傻分不清。…...
半小时学会HTML5
一、了解几个概念 1、HTML定义 HTML是(Hyper Text Markup Language)超文本标记语言,超文本包含:文字、图片、音频、视频、动画等。 2、W3C 是什么? W3C 即(World Wide Web Consortium) 万维…...
研报精选230421
目录 【行业230421南京证券】氢能行业:地缘政治加速绿色能源转型 【行业230421华安证券】AIGC行业研究框架与投资逻辑 【行业230421信达证券】工控行业深度报告:行业拐点将至,国产品牌加速崛起 【个股230421国信证券_华阳集团】聚焦汽车智能化…...
AI绘图风格对照表/画风样稿详细研究记录及经验总结(分析Midjourney和Stable Diffusion风格提示词实际使用情况)不断更新中...
Midjourney和Stable Diffusion都可以通过输入文本生成出令人惊叹的AI图像。 Midjourney是一个收费的在线服务,通过discord对话的形式来生图,局限性较大,但由于后台官方模型做得好,因此出图效果非常完美; Stable Diffus…...
人工智能论文的风格特点
搞清楚AI领域论文的风格特点是写出一篇高质量AI论文的前提,AI领域的论文有如下显著特点。 1. 论文的架构非常清晰且富有逻辑。一篇高质量的AI论文,读者通过大致扫一眼论文的各级标题就能够对论文的写作思路形成清晰的认识,明白论文各部分之间…...
成功上岸国防科大!
Datawhale干货 作者:王洲烽,太原理工大学,Datawhale成员 写在前面 相比较于一般的经验贴,我更想在这里讲述一下自己的故事。我一开始报考的是北理工,但很遗憾9月份北理改考408了,无缘京爷,所以…...
【C语言】输入输出、字符串操作、内存操作、文件操作函数
三对基本输入输出函数 1.gets(),puts() gets()从标准输入中获取一个字符串,到str(自己创建的char型数组)中,读到换行或输入末尾结束获取r; 成功返回str,失败返回空。 char *gets(char *str)…...
[golang gin框架] 25.Gin 商城项目-配置清除缓存以及前台列表页面数据渲染公共数据
配置清除缓存 当进入前台首页时,会缓存对应的商品相关数据,这时,如果后台修改了商品的相关数据,缓存中的对应数据并没有随之发生改变,这时就需要需改对应的缓存数据,这里有两种方法: 方法一 在管理后台操作直接清除缓存中的所有数据,当再次访问前台首页时,就会先从数据库中获取…...
文件夹改名,如何在改名之后批量复制文件夹名称
在日常时候中会遇到给文件夹改名的时候,那么我们又如何在改名之后批量复制文件夹名称?今天就由小编来给大家分享一下操作办法。 首先第一步,我们要进入文件批量改名高手,并在板块栏里选择“文件夹批量改名”板块。 第二步…...
汇编与内联 x86-64
机器字长 x86是32位系统 64是64位系统 这里的32和64,指的都是机器字长 机器字长是 能直接进行整数/位运算的大小指针的大小(索引内存的范围) 容易与机器字长混淆的概念:字 字 字存储字长 字是MDR寄存器的位数,代表每个主存存储体中的存储…...
OSCP-UT99(IRC、Unreal Tournament 99)
目录 扫描 WEB IRC 提权 扫描 sudo nmap 192.168.142.44 -p- -sS -sV PORT STATE SERVICE VERSION 21/tcp open ftp FileZilla ftpd 80/tcp open http Apache httpd 2.4.16 (OpenSSL/1.0.1p PHP/5.6.12) 44…...
Kubernetes CPU内存资源限定
在 Kubernetes 中创建工作负载时,您可以为 Pod 中的每一个容器指定其所需要的内存(RAM)大小和 CPU 数量。如果这些信息被指定了,Kubernetes 调度器可以更好的决定将 Pod 调度到哪一个节点。对于容器来说,其所需要的资源…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
Android第十三次面试总结(四大 组件基础)
Activity生命周期和四大启动模式详解 一、Activity 生命周期 Activity 的生命周期由一系列回调方法组成,用于管理其创建、可见性、焦点和销毁过程。以下是核心方法及其调用时机: onCreate() 调用时机:Activity 首次创建时调用。…...
深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用
文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么?1.1.2 感知机的工作原理 1.2 感知机的简单应用:基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
LabVIEW双光子成像系统技术
双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制,展现出显著的技术优势: 深层组织穿透能力:适用于活体组织深度成像 高分辨率观测性能:满足微观结构的精细研究需求 低光毒性特点:减少对样本的损伤…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...
Vue 3 + WebSocket 实战:公司通知实时推送功能详解
📢 Vue 3 WebSocket 实战:公司通知实时推送功能详解 📌 收藏 点赞 关注,项目中要用到推送功能时就不怕找不到了! 实时通知是企业系统中常见的功能,比如:管理员发布通知后,所有用户…...
leetcode_69.x的平方根
题目如下 : 看到题 ,我们最原始的想法就是暴力解决: for(long long i 0;i<INT_MAX;i){if(i*ix){return i;}else if((i*i>x)&&((i-1)*(i-1)<x)){return i-1;}}我们直接开始遍历,我们是整数的平方根,所以我们分两…...
【工具教程】多个条形码识别用条码内容对图片重命名,批量PDF条形码识别后用条码内容批量改名,使用教程及注意事项
一、条形码识别改名使用教程 打开软件并选择处理模式:打开软件后,根据要处理的文件类型,选择 “图片识别模式” 或 “PDF 识别模式”。如果是处理包含条形码的 PDF 文件,就选择 “PDF 识别模式”;若是处理图片文件&…...
