# Lab 2D

lab 2D 是实现日志压缩的

assignment指示完成三个函数：Snapshot、CondInstallSnapshot和InstallSnapshot RPC

### Snapshot

Snapshot函数是 上层与 raft来交互的，上层调用Snapshot函数来让leader进行snapshot。

这个函数有几个比较重要的rules ：

> * 如果 index <= lastSnapshotIndex ，那说明这个snotshop请求是过期的，可能由于各种网络原因之类的引起，这时候就要拒绝这个请求
> * 要进行snapshot丢弃的日志，首先应该已经被 commit 且 apply了
> * 需要持久化快照信息

### CondInstallSnapshot

CondInstallSnapshot这个函数可以设计为永远只返回true

在向applyCh写入一条消息后，会调用CondInstallSnapshot()接口是用来判断是否可以实施快照的，比如判断一下快照的index是否小于节点的commitIndex。

如 config.go/applierSnap()：

```
接收到一条 applyCh {
    if cfg.rafts[i].CondInstallSnapshot(m.SnapshotTerm,
                                        m.SnapshotIndex, m.Snapshot) {
        cfg.logs[i] = make(map[int]interface{})
        r := bytes.NewBuffer(m.Snapshot)
        d := labgob.NewDecoder(r)
        var v int
        if d.Decode(&v) != nil {
            log.Fatalf("decode error\n")
        }
        cfg.logs[i][m.SnapshotIndex] = v
        lastApplied = m.SnapshotIndex
    }
}
```

因为在 lab2B 实现中，往 applych发送log entry时是不持有锁的

> 因为教授说过，在发送 applych的时候不能持有锁。
>
> 上层实现时是在接收到一定的log entry 时进行snapshot，在snapshot返回时service不会进行任何操作，如果此时锁还在发送 log entry 的channel那里，进行 snapshot又需要获取锁，所以会造成死锁。

因此lab assignment选择让 log entry 和 snapshot 并发执行，这样子你就不能确定service什么时候能接收到snapshot了。

我们必须引入CondInstallSnapshot函数，确定service接收到snapshot的时间。

### InstallSnapshot

InstallSnapshot 也有几个重要的规则：

> * 如果 leader term 小于自己的term，就立刻拒绝
> * snapshot也相当于一次心跳，需要重置选举时间等心跳操作
> * 如果发送过来的快照落后于当前已经应用的快照，那就直接拒绝
> * follower应用成功后需要更新 commitIndex 和 lastapplied
> * 如果leader 发送snapshot成功，需要更新 发送给那个 server的对应的matchIndex和nextIndex

<br>
