TinyKV lab2b完成总结
作者:互联网
lab2b
集群视图下的日志的复制和状态的写入。
与lab2a相比,lab2b需要阅读大量代码了解整个集群是怎么工作的。首先,我们要对整个集群有个大概的了解(可以看文档)。
node和store看作等价的就行
可以看到,一个node上面可以跑多个Raft实例(叫做Peer/RaftGroup),每个Peer属于不同的Region,每个Region管理不同的key。他们之间的关系如下:
一个store可以有多个Region,每个Region之间的key space不重叠
一个store上可以有多个Peer,每个Peer对应一个Region
一个Region可以跨越多个store,可以由多个Peer组成
在lab2b里面,不会有一个store包含多个Region的情况
我们大概了解一下各种各样的key和state,这里官方已经给出了一个很详细的表了。这里需要注意的是写入的是kv还是raft数据库,以及这些state是每次有更新就得去写入。然后是消息写入流程,消息的写入是通过peer读取RawNode返回的Ready的消息封装成peer的消息然后调用Send方法放进了context中的transport接口,然后通过transport的router保存的集群节点信息发送到对应的节点,节点的HandleMsg函数会根据消息类型,如果是Raft消息就调用RawNode的Step函数。如果是RaftCMD消息,比如说是Get/Put/Delete,则调用proposeRaftCMD函数,这个就是我们需要实现的地方。除此之外,我们还要实现HandleRaftReady函数。
proposeRaftCMD函数
这个函数的功能是把客户端的命令的数据写入Raft状态机。我们先看key是不是当前region(只有msg.Request有key,AdminRequest没有),如果不是则返回RegionErr(lab2b还不需要考虑AdminRequest),否则反序列化成Raft状态机消息,并设置d.proposal(我们要通过d.proposal的callback保存响应返回上层,设计成这样子是可以异步返回响应而不会阻塞等待Raft完成日志复制过程),最后调用RawNode.Propose()。
func (d *peerMsgHandler) proposeRaftCommand(msg *raft_cmdpb.RaftCmdRequest, cb *message.Callback) {
err := d.preProposeRaftCommand(msg)
if err != nil {
cb.Done(ErrResp(err))
return
}
// Your Code Here (2B).
if msg.AdminRequest == nil {
key := getReqKey(msg.Requests[0])
if key != nil {
if err := util.CheckKeyInRegion(key, d.Region()); err != nil {
cb.Done(ErrResp(err))
return
}
}
d.proposals = append(d.proposals, &proposal{
index: d.nextProposalIndex(),
term: d.Term(),
cb: cb,
})
}
dat, err := msg.Marshal()
if err != nil {
panic(err)
}
err = d.RaftGroup.Propose(dat)
if err != nil {
panic(err)
}
}
HandleRaftReady函数
HandleRaftReady是集群对Raft的日志进行处理的函数,包括保存Ready的状态到存储器和设置RaftLog的一些值。首先看保存Ready状态。这里需要处理Ready中的HardState(SoftState不需要保存)和追加日志,注意这些日志当中可能有已经保存到存储器的日志了,我们需要删除掉这一部分,然后设置RaftLog的值
// Append the given entries to the raft log and update ps.raftState also delete log entries that will
// never be committed
func (ps *PeerStorage) Append(entries []eraftpb.Entry, raftWB *engine_util.WriteBatch) error {
// Your Code Here (2B).
if len(entries) == 0 {
return nil
}
psLast, _ := ps.LastIndex()
pos := 0
for _, e := range entries {
if e.Index > psLast {
break
}
pos++
}
entries = entries[pos:]
if len(entries) == 0 {
return nil
}
for _, e := range entries {
raftWB.SetMeta(meta.RaftLogKey(ps.region.Id, e.Index), &e)
}
ps.raftState.LastIndex = entries[len(entries)-1].Index
ps.raftState.LastTerm = entries[len(entries)-1].Term
return nil
}
然后把HardState和日志写到存储器
// Save memory states to disk.
// Do not modify ready in this function, this is a requirement to advance the ready object properly later.
func (ps *PeerStorage) SaveReadyState(ready *raft.Ready) (*ApplySnapResult, error) {
// Hint: you may call `Append()` and `ApplySnapshot()` in this function
// Your Code Here (2B/2C).
wb := &engine_util.WriteBatch{}
var res *ApplySnapResult
var err error
if err = ps.Append(ready.Entries, wb); err != nil {
return nil, err
}
if !raft.IsEmptyHardState(ready.HardState) {
ps.raftState.HardState = &ready.HardState
}
wb.SetMeta(meta.RaftStateKey(ps.region.Id), ps.raftState)
wb.WriteToDB(ps.Engines.Raft)
return res, err
}
完成以上部分,再注意细节,我们就可以通过lab2b的测试。
标签:总结,ps,nil,err,Region,TinyKV,key,entries,lab2b 来源: https://www.cnblogs.com/mchxyz/p/16023469.html