简介
lab3主要是用lab2实现的raft做一个容错的键值存储服务。lab3A主要是实现一个没有日志压缩功能的键值存储系统。
理想情况下的kvraft
思路
首先实现一个不存在消息丢失情况下的解决方案。
Client
Clerk记录一个leaderId
,每次发送RPC时从leaderId
开始。需要不断重复发送RPC,直至请求成功。
Server
存储k/v的是一个简单的map[string]string
;
PutAppend
、Get
方法调用底层raft的Start
方法并在对应的通道上等待消息;
在server设置listener
方法,负责监听applyCh
,当从applyCh
得到一个消息时,执行相应的操作(Put
、Append
)并通知正在等待的goroutine;
实现
common.go
package kvraft
const (
OK = "OK"
ErrNoKey = "ErrNoKey"
ErrWrongLeader = "ErrWrongLeader"
)
type Err string
// Put or Append
type PutAppendArgs struct {
Key string
Value string
Op string // "Put" or "Append"
// You'll have to add definitions here.
// Field names must start with capital letters,
// otherwise RPC will break.
}
type PutAppendReply struct {
Err Err
}
type GetArgs struct {
Key string
// You'll have to add definitions here.
}
type GetReply struct {
Err Err
Value string
}
client.go
package kvraft
import "../labrpc"
import "crypto/rand"
import "math/big"
type Clerk struct {
servers []*labrpc.ClientEnd
// You will have to modify this struct.
leaderId int
}
func nrand() int64 {
max := big.NewInt(int64(1) << 62)
bigx, _ := rand.Int(rand.Reader, max)
x := bigx.Int64()
return x
}
func MakeClerk(servers []*labrpc.ClientEnd) *Clerk {
ck := new(Clerk)
ck.servers = servers
// You'll have to add code here.
return ck
}
//
// fetch the current value for a key.
// returns "" if the key does not exist.
// keeps trying forever in the face of all other errors.
//
// you can send an RPC with code like this:
// ok := ck.servers[i].Call("KVServer.Get", &args, &reply)
//
// the types of args and reply (including whether they are pointers)
// must match the declared types of the RPC handler function's
// arguments. and reply must be passed as a pointer.
//
func (ck *Clerk) Get(key string) string {
args := GetArgs{
Key: key}
reply := GetReply{
}
for {
ok := ck.servers[ck.leaderId].Call("KVServer.Get", &args, &reply)
if ok && reply.Err == OK {
break
}
ck.leaderId = (ck.leaderId + 1) % len(ck.servers)
}
return reply.Value
}
//
// shared by Put and Append.
//
// you can send an RPC with code like this:
// ok := ck.servers[i].Call("KVServer.PutAppend", &args, &reply)
//
// the types of args and reply (including whether they are pointers)
// must match the declared types of the RPC handler function's
// arguments. and reply must be passed as a pointer.
//
func (ck *Clerk) PutAppend(key string, value string, op string) {
// You will have to modify this function.
args := PutAppendArgs{
Key: key,
Value: value,
Op: op,
}
reply := PutAppendReply{
}
for {
ok := ck.servers[ck.leaderId].Call("KVServer.PutAppend", &args, &reply)
if ok && reply.Err == OK {
return
}
ck.leaderId = (ck.leaderId + 1) % len(ck.servers)
}
}
func (ck *Clerk) Put(key string, value string) {
ck.PutAppend(key, value, "Put")
}
func (ck *Clerk) Append(key string, value string) {
ck.PutAppend(key, value, "Append")
}
server.go
package kvraft
import (
"../labgob"
"../labrpc"
"../raft"
"log"
"sync"
"sync/atomic"
)
const Debug = 0
func DPrintf(format string, a ...interface{
}) (n int, err error) {
if Debug > 0 {
log.Printf(format, a...)
}
return
}
type Notification int
const (
Success = iota
Fail
)
type Op struct {
// Your definitions here.
// Field names must start with capital letters,
// otherwise RPC will break.
NotificationCh chan Notification
Type string
Key string
Value string
}
type KVServer struct {
mu sync.Mutex
me int
rf *raft.Raft
applyCh chan raft.ApplyMsg
dead int32 // set by Kill()
maxraftstate int // snapshot if log grows this big
// Your definitions here.
db map[string]string
}
func (kv *KVServer) Get(args *GetArgs, reply *GetReply) {
// Your code here.
op := Op{
NotificationCh: make(chan Notification),
Type: "Get",
Key: args.Key,
}
kv.mu.Lock()
_, _, isLeader := kv.rf.Start(op)
DPrintf("[%d] starts a command, command = %v", kv.me, op)
kv.mu.Unlock()
if !isLeader {
reply.Err = ErrWrongLeader
return
}
no := <-op.NotificationCh
DPrintf("[%d] receives notification from its notification channel, no = %v", kv.me, no)
if no == Fail {
reply.Err = ErrWrongLeader
return
}
// no == Success
kv.mu.Lock()
reply.Value = kv.db[op.Key]
kv.mu.Unlock()
reply.Err = OK
}
func (kv *KVServer) PutAppend(args *PutAppendArgs, reply *PutAppendReply) {
// Your code here.
op := Op{
NotificationCh: make(chan Notification),
Type: args.Op,
Key: args.Key,
Value: args.Value