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 调度到哪一个节点。对于容器来说,其所需要的资源…...
uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖
在前面的练习中,每个页面需要使用ref,onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入,需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
docker 部署发现spring.profiles.active 问题
报错: org.springframework.boot.context.config.InvalidConfigDataPropertyException: Property spring.profiles.active imported from location class path resource [application-test.yml] is invalid in a profile specific resource [origin: class path re…...
关键领域软件测试的突围之路:如何破解安全与效率的平衡难题
在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件,这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下,实现高效测试与快速迭代?这一命题正考验着…...
电脑桌面太单调,用Python写一个桌面小宠物应用。
下面是一个使用Python创建的简单桌面小宠物应用。这个小宠物会在桌面上游荡,可以响应鼠标点击,并且有简单的动画效果。 import tkinter as tk import random import time from PIL import Image, ImageTk import os import sysclass DesktopPet:def __i…...
二叉树-144.二叉树的前序遍历-力扣(LeetCode)
一、题目解析 对于递归方法的前序遍历十分简单,但对于一位合格的程序猿而言,需要掌握将递归转化为非递归的能力,毕竟递归调用的时候会调用大量的栈帧,存在栈溢出风险。 二、算法原理 递归调用本质是系统建立栈帧,而非…...
持续交付的进化:从DevOps到AI驱动的IT新动能
文章目录 一、持续交付的本质:从手动到自动的交付飞跃关键特性案例:电商平台的高效部署 二、持续交付的演进:从CI到AI驱动的未来发展历程 中国…...
分布式光纤声振传感技术原理与瑞利散射机制解析
分布式光纤传感技术(Distributed Fiber Optic Sensing,简称DFOS)作为近年来迅速发展的新型感知手段,已广泛应用于边界安防、油气管道监测、结构健康诊断、地震探测等领域。其子类技术——分布式光纤声振传感(Distribut…...
Docker 镜像上传到 AWS ECR:从构建到推送的全流程
一、在 EC2 实例中安装 Docker(适用于 Amazon Linux 2) 步骤 1:连接到 EC2 实例 ssh -i your-key.pem ec2-useryour-ec2-public-ip步骤 2:安装 Docker sudo yum update -y sudo amazon-linux-extras enable docker sudo yum in…...
