etcd raft 处理流程图系列2-wal的读写
本文仅介绍wal的基本处理,如create、open、close、read等操作。鉴于篇幅原因,下面介绍replayWAL(启动raft节点时执行)函数涉及的读文件操作:从wal目录中加载snapshot,wal文件的创建,以及读取wal目录中的所有数据(主要是entryType、stateType、metadataType这几类)。
WAL的处理还是比较复杂的可以借鉴的地方也很多。WAL在编码以及flush时使用缓存来提升效率。flush的单位为分页,每页又分为8个section,section的作用是用来检测写入的数据是否被破坏,检测逻辑为:如果某个section中的所有字节都为0,则说明数据遭到破坏,反之则认为数据正常。在isTornEntry中,主要通过section机制来检测WAL文件中最后一个record是否因为数据破坏而导致json解析或crc校验失败。
创建
下面是wal的create流程,在创建文件事先预分配文件大小(64MB),用于提升性能。wal通过encode()函数将编码后的数据写入文件,因此需要在对文件执行写操作时加锁,写入的数据以record为单位(record首先被写入缓存,当数据以页为单位对齐时通过flush写入文件)。先计算数据的crc校验码,然后计算record的帧数据。写数据时,先写入帧数据,再写入record。在写入数据(无论是帧数据还是record)时,会以页为单位将数据写入文件,不足一页的数据会暂存在缓存中。帧数据保存了实际的数据大小和pad的数据大小,在读取wal文件时会用到该信息。
wal的文件名由两部分构成:seq和index,前者应该顺序递增的,以保证日志文件的连续性(isValidSeq会根据seq校验日志文件的连续性)。

加载snapshot
下面是在wal目录中加载snapshot的操作,该操作中用到了上面的帧数据。wal使用decode()函数进行解码,首先取出在帧数据中解析出record的大小和padBytes的小,然后根据record的大小解码数据,最后根据record的类型采集并返回所有snapshot。

从上面可以看到,wal的encoder用于写文件,因此encoder会关联到当前正在编辑的文件,记录了文件句柄、当前字节偏移以及缓存等信息,一般会选择WAL.locks中的最后一个元素。而decoder用于读取所有文件,因此关联到多个wal文件,记录了这些文件句柄。
读取所有数据
下图是从wal目录中尝试读取所有信息(如metadata、entries、state)的过程。涉及读取wal目录中的文件信息,以此构建WAL结构,然后通过生成的decoder来将文件解码为不同类型的数据进行处理。最终返回解码后的数据。需要注意decoder的文件是有序的,可以从源码fileutil.ReadDir看出来,其对文件名进行了sort.Strings(names)操作。
此外,在读取文件时,根据文件的读写模式分别进行了处理。读模式下只需读完所有文件,关闭文件并返回结果即可。写模式下文件是加锁的,在decodeRecord中会读取lastValidOff(frameSizeBytes + recBytes + padBytes)长度的数据,并将该长度之后的数据归0,防止文件中出现被破坏的数据,由于对文件的修改会改变文件的crc校验,但好在新的record不会立即刷新到文件中(源码中的描述如下),更新文件的encoder,后续通过encoder将数据最终写入文件即可。
// decodeRecord() will return io.EOF if it detects a zero record,
// but this zero record may be followed by non-zero records from
// a torn write. Overwriting some of these non-zero records, but
// not all, will cause CRC errors on WAL open. Since the records
// were never fully synced to disk in the first place, it's safe
// to zero them out to avoid any CRC errors from new writes.

WAL的保存
raftexample的serveChannels中当接收到node.Ready()传来的数据时,会对这些数据进行持久化。如下图,首先会保存状态和entry信息,如果locks中最后一个文件(该文件)的内容大于或等于SegmentSizeBytes时需要切割文件。
在切分文件时,将已有的数据同步到文件中,后面的操作就是新建一个文件。新文件来自于WAL.fp是在重命名文件或创建文件时创建的,可以看到一个WAL只有一个fp,用于在切割文件时提高效率。
首先在新文件中记录当前的crc,然后写入metadata和state信息,并重新计算crc,在读取时可以校验到此为止的crc。新文件作为WAL.locks中的最后一个文件。

etcd raft 处理流程图系列2-wal的读写的更多相关文章
- etcd raft 处理流程图系列1-raftexample
最近在看raft相关的代码和实现,发现etcd的raft模块在实现上还是比较灵活的,但缺点就是需要用户实现比较多的功能,如存储和网络等,同时带来的优点就是不会对用户的存储和传输作限制.网上对该模块的描 ...
- etcd raft 处理流程图系列3-wal的存储和运行
存储和节点的创建 raftexample中的存储其实有两种,一个是通过raft.NewMemoryStorage()进行创建的raft.raftStorage,关联到单个raft节点,另一个是通过ne ...
- etcd raft 处理流程图系列2-transport
本章给出了raftexample中使用的传输层代码,补全了上一节中传输层与raft节点(raft server和channel server)的交互细节.下图中流程的核心在于传输层中的streamRt ...
- etcd raft library设计原理和使用
早在2013年11月份,在raft论文还只能在网上下载到草稿版时,我曾经写过一篇blog对其进行简要分析.4年过去了,各种raft协议的讲解铺天盖地,raft也确实得到了广泛的应用.其中最知名的应用莫 ...
- 彻底搞懂etcd raft选举、数据同步
etcd raft选举机制 etcd 是一个分布式的k/V存储系统.核心使用了RAFT分布式一致性协议.一致性这个概念,它是指多个服务器在状态达成一致,但是在一个分布式系统中,因为各种意外可能,有的服 ...
- etcd raft如何实现成员变更
成员变更在一致性协议里稍复杂一些,由于不同的成员不可能在同一时刻从旧成员组切换至新成员组,所以可能出现两个不相交的majority,从而导致同一个term出现两个leader,进而导致同一个index ...
- etcd raft library
https://github.com/coreos/etcd/tree/master/raft import "github.com/coreos/etcd/raft" ----- ...
- etcd raft如何实现leadership transfer
leadership transfer可以把raft group中的leader身份转给其中一个follower.这个功能可以用来做负载均衡,比如可以把leader放在性能更好的机器或者离客户端更近的 ...
- etcd raft如何实现Linearizable Read
Linearizable Read通俗来讲,就是读请求需要读到最新的已经commit的数据,不会读到老数据. 对于使用raft协议来保证多副本强一致的系统中,读写请求都可以通过走一次raft协议来满足 ...
随机推荐
- 创建Akamai cdn api授权
注:通过Akamai Cli purge和通过Akamai API进行刷新之前,都要事先创建类似于如下的刷新的凭据,这两种刷新方式所创建的凭据是相同的. 目的:创建Akamai CDN API授权以便 ...
- 深入理解 Go Map
文章参考:Go语言设计与实现3.3 哈希表 哈希表的意义不言而喻,它能提供 O(1) 复杂度的读写性能,所以主流编程语言中都内置有哈希表. 哈希表的关键在于哈希函数, 好的哈希函数能减少哈希碰撞,提供 ...
- 学习vue遇到的第一个小怪兽:net::err_file_not_found
问题现象 引入vue.js文件时报错:net::err_file_not_found,我用的是VScode开发工具,调试工具用的Chrome. 解决方案 1.检查路径是否正确 2.检查文件是否有问题 ...
- Springboot:Springboot+mysql5.7搭建服务,超过8小时连接mysql失败
报错信息 2017-03-12 03:00:02.539 ERROR 9311 --- [nio-9000-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] ...
- Mysql/Oracle/达梦中数据字典表
在mysql中,数据字典表存放在information_schema库中,,对应的两张表,分别是TABLES,和COLUMNS. 在oracle中有表空间(即用户空间或者库)和模式的概念.模式和表空间 ...
- gitlab找回管理员密码
1.登陆后台服务器,切换git用户 su - git 2.登录GitLab的Rails控制台 gitlab-rails console production 另一种 切换root账户 执行: git ...
- MySQL之group by的使用
使用命令:select 字段 from table_name group by col_name; 注意点: 在有group by的查询语句中,select指定的字段要么就包含在group by语句的 ...
- FastTunnel-开源内网穿透框架
FastTunnel - 打造人人都能搭建的内网穿透工具 FastTunnel是用.net core开发的一款跨平台内网穿透工具,它可以实现将内网服务暴露到公网供自己或任何人访问. 与其他穿透工具不同 ...
- ESP32-mqtt笔记
基于ESP-IDF4.1 #include <stdio.h> #include <stdint.h> #include <stddef.h> #include & ...
- QT从入门到入土(一)——Qt5.14.2安装教程和VS2019环境配置
引言 24岁的某天,承载着周围人的关心,一路南下.天晴心静,听着斑马,不免对未来有些彷徨.但是呢,人生总要走陌生的路,看陌生的风景,所幸可以听着不变的歌,关心自己的人就那么多.就像是对庸常生活的一次越 ...