wal

package
v0.0.0-...-1337258 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 20, 2024 License: Apache-2.0 Imports: 19 Imported by: 0

Documentation

Overview

Package wal 实现预写日志(Write-Ahead-Log)

Log 致力于对外提供高性能读、写、截断日志服务,这些操作通过内部加锁保证外部 并发调用不会出现竞态条件,需要注意的是:Open 操作会有竞态问题,请避免并发 打开日志,或者外部主动加锁避免竞态条件。

Log 提供两种数据持久化级别:

  1. 同步:每次写日志都会将数据同步到磁盘,一致性好,但性能差。
  2. 异步:日志会先写入内存缓冲中,后台协程周期同步,一致性稍差,但性能优异。

Log 允许通过 Options 自定义配置选项,配置包括权限、容量、持久化级别、同步 周期。下面简要介绍其中涉及到的内部实现概念:

  1. Block:描述单条日志记录的逻辑概念。用于定位日志记录边界、记录完整性校验、 限制 Log 下最大日志数量(通过maxBlockCapacityInWAL)。
  2. Segment:日志数据文件,由零或多个 Block 组成。一个 Log 中通常有多个 Segment 这是出于变更 Segment 文件时写时复制(Copy-On-Write)的性能考虑,这意味着 Segment 保证原子写入文件。Segment 允许自定义容量(可通过 Options 配置), 但这不意味只有当一个数据文件写满后才会创建下一个,Segment 会保证 Block完整地 存在一个数据文件中。
  3. Log:先行日志实体,由多个 Segment 组成。Log 将 Segment 维护在指定目录 下(可通过 Options 配置)。内部通过 LRU 缓存最近读命中的 Segment,同样的, 可以通过 Options 配置 LRU 缓存大小。

TODO(Trino):日志损坏后没有修复机制,如果出现内容被篡改、损坏会导致日志不可用

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Log

type Log struct {
	// contains filtered or unexported fields
}

Log 预写日志

func Open

func Open(dirPath string, opts *Options) (_ *Log, err error)

Open 打开wal

为了避免内存泄漏,结束使用后需要显示调用 Log.Close 关闭

参数:

  • dirPath 存放 Log 日志的目录
  • opts 配置选项,不传配置将设置成默认值,见 NewOptions

返回值:

  • wal日志(*Log)
  • errs 过程中出现的错误,类型是 *errs.KvErr

异常:

  • errs.NewFlockFileErr Open 指定的dir已经被其他 Log 实例打开, 此时外部应该检查这个错误并且重试其他目录路径;有小概率是因为初始化 Log 实例过程中出现其他错误(会在日志中输出)之后释放目录锁导致
  • errs.NewInvalidParamErr 传入 opts 校验未通过,日志中有描述具体非法参数; 也有可能是传入的目录路径不是一个目录
  • errs.NewMkdirErr 配置中目录不存在,且尝试创建目录失败
  • errs.NewFileStatErr 获取目录文件信息失败
  • errs.NewOpenFileErr 打开目录锁文件、数据失败
  • errs.NewWalkDirErr 遍历日志目录失败,具体原因请参考日志
  • errs.NewParseIntErr 解析 segment 文件名失败
  • errs.NewReadFileErr 读取 segment 文件失败
  • errs.NewCorruptErr segment 数据被篡改、损坏
  • errs.NewBackgroundErr 这个错误不会抛出,但会出现在日志中,表示后台协程执行出错

func (*Log) Close

func (wal *Log) Close() error

Close 关闭 Log,释放资源

返回值:

  • errs 过程中出现的错误,类型是 *errs.KvErr

异常:

  • errs.NewFileClosedErr 在 Log 或 segment 已经关闭后再次尝试关闭
  • errs.NewFlockFileErr 释放目录锁失败

func (*Log) Len

func (wal *Log) Len() (int64, error)

Len 获取 Log 日志中存储的日志数量,即 block 数量

返回值:

  • number 日志数量
  • errs 过程中出现的错误,类型是 *errs.KvErr

异常:

  • errs.NewFileClosedErr 在wal已经关闭的情况下调用
  • errs.NewCorruptErr 在wal数据已经被破坏的情况下调用
  • errs.NewBackgroundErr 在wal后台协程执行失败的情况下调用

func (*Log) Read

func (wal *Log) Read(idx int64) (map[int64][]byte, error)

Read 读指定范围内的日志

当出现日志循环时,firstBlockIdx 比合法的 idx 大 此时的读取范围是 [0, idx) & [firstBlockIdx, maxBlockCapacityInWAL] 截断后 firstBlockIdx 会发生改变

参数:

  • idx 指定读取 firstBlockIdx ~ idx 范围内的日志记录

返回值:

  • blockIdxToData 查询到的日志数据映射,blockIdx -> data
  • errs 过程中出现的错误,类型是 *errs.KvErr

异常:

  • errs.NewFileClosedErr 在wal已经关闭的情况下读取
  • errs.NewCorruptErr 在wal数据已经被破坏的情况下读取
  • errs.NewBackgroundErr 在wal后台协程执行失败的情况下读取
  • errs.NewNotFoundErr 传入idx不在有效范围内
  • errs.NewOpenFileErr 打开目录锁文件、数据失败
  • errs.NewReadFileErr 读取 segment 文件失败
  • errs.NewCorruptErr 在wal数据已经被破坏的情况下写入

func (*Log) Sync

func (wal *Log) Sync() error

Sync 同步内存中的日志数据到磁盘中

返回值:

  • errs 过程中出现的错误,类型是 *errs.KvErr

异常:

  • errs.NewFileClosedErr 在 Log、segment 已经关闭的情况下同步
  • errs.NewCorruptErr 在wal数据已经被破坏的情况下同步
  • errs.NewBackgroundErr 在wal后台协程执行失败的情况下同步
  • errs.NewCreateTempFileErr 创建临时文件失败
  • errs.NewSeekFileErr 重定位 segment 文件偏移量失败
  • errs.NewCopyFileErr 拷贝 segment 文件内容失败
  • errs.NewCloseFileErr 关闭 segment 文件失败
  • errs.NewWriteFileErr 写入 segment 文件失败
  • errs.NewSyncFileErr 同步 segment 文件到磁盘失败

func (*Log) Truncate

func (wal *Log) Truncate(idx int64) error

Truncate 截断指定范围内的日志

当出现日志循环时,firstBlockIdx 比指定的 idx 大 此时的截断范围是 [0, idx) & [firstBlockIdx, maxBlockCapacityInWAL] 截断后 firstBlockIdx 会发生改变

参数:

  • idx 指定截断 firstBlockIdx ~ idx 范围内的日志记录

返回值:

  • errs 过程中出现的错误,类型是 *errs.KvErr

异常:

  • errs.NewFileClosedErr 在wal已经关闭的情况下写入
  • errs.NewCorruptErr 在wal数据已经被破坏的情况下写入
  • errs.NewBackgroundErr 在wal后台协程执行失败的情况下写入
  • errs.NewInvalidParamErr 单次写入数据超过 segment 文件容量或写入空数据
  • errs.NewRenameFileErr 重命名 segment 文件失败
  • errs.NewCreateTempFileErr 创建临时文件失败
  • errs.NewSeekFileErr 重定位 segment 文件偏移量失败
  • errs.NewCopyFileErr 拷贝 segment 文件内容失败
  • errs.NewCloseFileErr 关闭 segment 文件失败
  • errs.NewWriteFileErr 写入 segment 文件失败
  • errs.NewSyncFileErr 同步 segment 文件到磁盘失败
  • errs.NewOpenFileErr 打开 segment 文件失败
  • errs.NewRemoveFileErr 移除 segment 文件失败

示例:

// 循环执行完成后,Log 中 firstBlockIdx 为0,blockId 的范围是 [0, 100]
for i := 0; i < 100; i++ {
	_, errs := wal.Write([]byte{1, 2, 3})
}

// 截断 [0, 50) 范围内的 block,此时 Log 中维护的 block 范围是 [50, 100],firstBlockIdx 是50
wal.truncate(50)

// 继续上面的例子,假设wal能够容纳的最大block数量是100,那么我们继续向wal中写的日志blockId会从0开始循环
// 循环执行完成后,wal中 firstBlockIdx 为50,blockId的范围是[0, 20] & [50, 100]
for i := 0; i < 20; i++ {
	_, errs := wal.Write([]byte{1, 2, 3})
}

// 截断 [0, 10) & [50, 100] 范围内的 block,此时 Log 中维护的 block 范围是 [10, 20],firstBlockIdx 是10
wal.truncate(10)

func (*Log) Write

func (wal *Log) Write(data []byte) (int64, error)

Write 写入日志数据

参数:

  • data 日志数据,类型是字节数组

返回值:

  • blockIdx block 索引,该值用于 Read 和 Truncate
  • errs 过程中出现的错误,类型是 *errs.KvErr

异常:

  • errs.NewWalFullErr Log 日志文件夹下存储内容已满,使用者应该主动检查该类型错误 并在 wal.Truncate 后重新尝试写入,此时 Log 不会关闭,因此对 Log 的操作是安全的
  • errs.NewFileClosedErr 在wal已经关闭的情况下写入
  • errs.NewCorruptErr 在wal数据已经被破坏的情况下写入
  • errs.NewBackgroundErr 在wal后台协程执行失败的情况下写入
  • errs.NewInvalidParamErr 单次写入数据超过 segment 文件容量或写入空数据
  • errs.NewRenameFileErr 重命名 segment 文件失败
  • errs.NewParseIntErr 解析 segment 文件名失败
  • errs.NewCreateTempFileErr 创建临时文件失败
  • errs.NewSeekFileErr 重定位 segment 文件偏移量失败
  • errs.NewCopyFileErr 拷贝 segment 文件内容失败
  • errs.NewCloseFileErr 关闭 segment 文件失败
  • errs.NewWriteFileErr 写入 segment 文件失败
  • errs.NewSyncFileErr 同步 segment 文件到磁盘失败

type Options

type Options struct {
	// contains filtered or unexported fields
}

Options 日志配置选项

func NewOptions

func NewOptions() *Options

NewOptions 初始化wal配置选项

返回值:

  • 接收器wal配置选项(*options)

func (*Options) SetDataPerm

func (opts *Options) SetDataPerm(dataPerm os.FileMode) *Options

SetDataPerm 设置 segment 数据文件权限位

该配置在创建新 segment 时会用到,已经存在的 segment 不会受到影响

参数:

  • dataPerm segment 数据文件权限位

返回值:

  • 接收器wal配置选项(*options)

func (*Options) SetDirPerm

func (opts *Options) SetDirPerm(dirPerm os.FileMode) *Options

SetDirPerm 设置 Log 日志目录权限位

如果在启动 Log 时发现指定dirPath下不存在wal目录,该配置会在新建目录时用到

参数:

  • dirPerm Log 目录权限位

返回值:

  • 接收器 Log 配置选项(*options)

func (*Options) SetNoSync

func (opts *Options) SetNoSync() *Options

SetNoSync 设置不要每次写入都持久化到磁盘

当设置为true时,刷盘时机有

  1. 外部主动调用 wal.Sync
  2. 当前 segment 文件写满时自动持久化数据到磁盘
  3. 后台协程每隔 syncInterval 毫秒周期刷盘

通常情况下设置 noSync 选项后性能会有可观提升

返回值:

  • 接收器 Log 配置选项(*options)

func (*Options) SetSegmentCacheSize

func (opts *Options) SetSegmentCacheSize(segmentCacheCapacity int) *Options

SetSegmentCacheSize 设置wal中的segment最大缓存数量

segment 缓存只有在读取日志内容时生效

参数:

  • segmentCacheCapacity LRU 中的 segment 最大缓存数量

返回值:

  • 接收器 Log 配置选项(*options)

func (*Options) SetSegmentCapacity

func (opts *Options) SetSegmentCapacity(segmentCapacity int64) *Options

SetSegmentCapacity 设置 segment 数据文件最大存储容量

segment 文件在写入时通常不会达到文件最大存储容量就开始写入下一个 segment 文件 这是因为写入 segment 的最细粒度是 block,单个 block 无法跨 segment 存储

参数:

  • segmentCapacity segment 数据文件最大存储容量

返回值:

  • 接收器 Log 配置选项(*options)

func (*Options) SetSyncInterval

func (opts *Options) SetSyncInterval(syncInterval time.Duration) *Options

SetSyncInterval 设置后台协程刷盘时间间隔,单位是毫秒

只有在设置 SetNoSync 选项后该选项才生效

参数:

  • syncInterval 后台协程刷盘时间间隔,单位是毫秒

返回值:

  • 接收器 Log 配置选项(*options)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL