Talent Plan TinyKV Project2B RaftKV

RaftStore、Region、Peer的关系如下。

image.png

Peer封装了RawNode和PeerStorage(与2A中的MemoryStorage一样,同为raft.Storage接口的实现)。

type peer struct {
    RaftGroup   *raft.RawNode
    peerStorage *PeerStorage
}
复制代码

2B从Raftstore.start()开始,它启动了一些raftWorker,主要做两件事情。

  1. 从Raft获取并处理Ready。
  2. 从raftCh获取并处理Msg。

获取并处理Ready

不同于MemoryStorage是在内存中持久化,PeerStorage使用badger做持久化。

type PeerStorage struct {
    Engines *engine_util.Engines
}
复制代码

Engines里面有两个badger实例,各自存储了一些信息。不同类型的信息使用特殊的前后缀用作区分。

  1. raftDB
    • raft log
      • key: region_id + log_idx
      • value: entry
    • raft state
      • key: region_id
      • value: RaftLocalState
  2. kvDB
    • data
    • apply state
      • key: region_id
      • value: RaftApplyState
    • region state
      • key: region_id
      • value: RegionLocalState

我们需要实现的是两个函数。

  1. PeerStorage.SaveReadyState(Ready) error
    • 使用Append保存Ready.Entries。
    • 更新PeerStorage.RaftLocalState.HardState并保存到raftDB。
  2. PeerStorage.Append([]pb.Entry, *engine_util.WriteBatch) error
    • 将Entires保存到raftDB,并删除现在Storage中在本轮Append最后一个Entry之后的所有Entries,同时更新PeerStorage.RaftLocalState并保存到raftDB。
    • 删除这里补充一下,我在2A的文章提过,stabled是会变化的,因此是可能出现之前Append的Entry在最新一次Append之后的情况。

SaveReadyState

image.png

Append

image.png

获取并处理Msg

我们需要实现的是两个函数。

  1. peerMsgHandler.HandleRaftReady()
  2. peerMsgHandler.proposeRaftCommand()

peerMsgHandler对外暴露两个函数。

  1. HandleMsgs

    • HandleMsgs处理一些消息,比如说MsgTypeTick驱动Raft,调用RawNode.Tick();再比如收到MsgTypeRaftCmd向Raft追加一条日志,调用proposeRaftCommand()。
  2. HandleRaftReady

    • HandleMsgs后,Raft状态可能会有变化,HandleRaftReady察觉变化的状态,并进行相应的动作,最后调用RawNode.Advance()。

proposeRaftCommand

image.png

HandleRaftReady

image.png

image.png

总结

其实2B应该和2AC放到一起,关联性比较大,关于Storage、Peer、Node、Store等等类似的概念比较多,嵌套的层级也很深,但其实把raftWorker.run()当做入口开始看就好。

2B是不涉及Snapshot的,但测试程序要从Response中拿到Snap,再从Snap中拿到Region,所以你还是需要在apply中考虑CmdType_Snap的情况。

猜你喜欢

转载自juejin.im/post/7041212303479406622