130 lines
3.1 KiB
Go
130 lines
3.1 KiB
Go
package shardkv
|
|
|
|
//
|
|
// client code to talk to a sharded key/value service.
|
|
//
|
|
// the client first talks to the shardctrler to find out
|
|
// the assignment of shards (keys) to groups, and then
|
|
// talks to the group that holds the key's shard.
|
|
//
|
|
|
|
import "6.5840/labrpc"
|
|
import "crypto/rand"
|
|
import "math/big"
|
|
import "6.5840/shardctrler"
|
|
import "time"
|
|
|
|
// which shard is a key in?
|
|
// please use this function,
|
|
// and please do not change it.
|
|
func key2shard(key string) int {
|
|
shard := 0
|
|
if len(key) > 0 {
|
|
shard = int(key[0])
|
|
}
|
|
shard %= shardctrler.NShards
|
|
return shard
|
|
}
|
|
|
|
func nrand() int64 {
|
|
max := big.NewInt(int64(1) << 62)
|
|
bigx, _ := rand.Int(rand.Reader, max)
|
|
x := bigx.Int64()
|
|
return x
|
|
}
|
|
|
|
type Clerk struct {
|
|
sm *shardctrler.Clerk
|
|
config shardctrler.Config
|
|
make_end func(string) *labrpc.ClientEnd
|
|
// You will have to modify this struct.
|
|
}
|
|
|
|
// the tester calls MakeClerk.
|
|
//
|
|
// ctrlers[] is needed to call shardctrler.MakeClerk().
|
|
//
|
|
// make_end(servername) turns a server name from a
|
|
// Config.Groups[gid][i] into a labrpc.ClientEnd on which you can
|
|
// send RPCs.
|
|
func MakeClerk(ctrlers []*labrpc.ClientEnd, make_end func(string) *labrpc.ClientEnd) *Clerk {
|
|
ck := new(Clerk)
|
|
ck.sm = shardctrler.MakeClerk(ctrlers)
|
|
ck.make_end = make_end
|
|
// 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 will have to modify this function.
|
|
func (ck *Clerk) Get(key string) string {
|
|
args := GetArgs{}
|
|
args.Key = key
|
|
|
|
for {
|
|
shard := key2shard(key)
|
|
gid := ck.config.Shards[shard]
|
|
if servers, ok := ck.config.Groups[gid]; ok {
|
|
// try each server for the shard.
|
|
for si := 0; si < len(servers); si++ {
|
|
srv := ck.make_end(servers[si])
|
|
var reply GetReply
|
|
ok := srv.Call("ShardKV.Get", &args, &reply)
|
|
if ok && (reply.Err == OK || reply.Err == ErrNoKey) {
|
|
return reply.Value
|
|
}
|
|
if ok && (reply.Err == ErrWrongGroup) {
|
|
break
|
|
}
|
|
// ... not ok, or ErrWrongLeader
|
|
}
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
// ask controller for the latest configuration.
|
|
ck.config = ck.sm.Query(-1)
|
|
}
|
|
|
|
return ""
|
|
}
|
|
|
|
// shared by Put and Append.
|
|
// You will have to modify this function.
|
|
func (ck *Clerk) PutAppend(key string, value string, op string) {
|
|
args := PutAppendArgs{}
|
|
args.Key = key
|
|
args.Value = value
|
|
args.Op = op
|
|
|
|
|
|
for {
|
|
shard := key2shard(key)
|
|
gid := ck.config.Shards[shard]
|
|
if servers, ok := ck.config.Groups[gid]; ok {
|
|
for si := 0; si < len(servers); si++ {
|
|
srv := ck.make_end(servers[si])
|
|
var reply PutAppendReply
|
|
ok := srv.Call("ShardKV.PutAppend", &args, &reply)
|
|
if ok && reply.Err == OK {
|
|
return
|
|
}
|
|
if ok && reply.Err == ErrWrongGroup {
|
|
break
|
|
}
|
|
// ... not ok, or ErrWrongLeader
|
|
}
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
// ask controller for the latest configuration.
|
|
ck.config = ck.sm.Query(-1)
|
|
}
|
|
}
|
|
|
|
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")
|
|
}
|