MIT 6.824/6.5840 分布式系统 Lab 2

#Lab 2: Key/Value Server

新版本 6.824 课程添加了一个 Lab 2,实现一个请求幂等的简单 KV 存储服务,还有对应的 Client 端。

幂等性:题目依赖的底层 RPC 网络是不可靠的。为此,在请求中携带一个递增的 Sequence Number,在服务端忽略每个 Client 的旧 Request 即可。

服务端实现:维护一个 map[int]int,记录每一个 Client 发来的最大的 Sequence Number;注意 Append 命令还需要缓存执行的结果。

1
2
3
4
5
6
7
8
type Last struct {
Seq int
Value string
}

type KVServer struct {
last map[int]Last
}

Get 命令无需考虑幂等性,直接返回即可:

1
2
3
4
5
func (kv *KVServer) Get(args *GetArgs, reply *GetReply) {
kv.mu.Lock()
defer kv.mu.Unlock()
reply.Value = kv.store[args.Key]
}

Put 命令需要保证幂等性:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
func (kv *KVServer) Put(args *PutAppendArgs, reply *PutAppendReply) {
// log.Println("Put", args)
kv.mu.Lock()
defer kv.mu.Unlock()
if last, ok := kv.last[args.ClientId]; ok {
if args.Seq < last.Seq {
panic("seq < last")
}
if args.Seq == last.Seq {
return
}
}
kv.last[args.ClientId] = Last{Seq: args.Seq}
kv.store[args.Key] = args.Value
}

Append 命令不仅需要保证幂等性,还需要缓存执行的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func (kv *KVServer) Append(args *PutAppendArgs, reply *PutAppendReply) {
// log.Println("Append", args)
kv.mu.Lock()
defer kv.mu.Unlock()
if last, ok := kv.last[args.ClientId]; ok {
if args.Seq < last.Seq {
panic("seq < last")
}
if args.Seq == last.Seq {
reply.Value = last.Value
return
}
}
// Reply with the old value
reply.Value = kv.store[args.Key]
kv.last[args.ClientId] = Last{Seq: args.Seq, Value: kv.store[args.Key]}
kv.store[args.Key] += args.Value
}