6.824 Lab 3A: Fault-tolerant Key/Value Service

简介

lab3主要是用lab2实现的raft做一个容错的键值存储服务。lab3A主要是实现一个没有日志压缩功能的键值存储系统。

理想情况下的kvraft

思路

首先实现一个不存在消息丢失情况下的解决方案。

Client

Clerk记录一个leaderId,每次发送RPC时从leaderId开始。需要不断重复发送RPC,直至请求成功。

Server

存储k/v的是一个简单的map[string]string
PutAppendGet方法调用底层raft的Start方法并在对应的通道上等待消息;
在server设置listener方法,负责监听applyCh,当从applyCh得到一个消息时,执行相应的操作(PutAppend)并通知正在等待的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
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值