Compare commits
10 Commits
420dd2208d
...
dffbcad936
| Author | SHA1 | Date | |
|---|---|---|---|
| dffbcad936 | |||
| 63706b6685 | |||
|
|
db55e49c10 | ||
|
|
ab9b8d2f68 | ||
|
|
f4310a86eb | ||
|
|
52bf53742e | ||
|
|
b6635d50e5 | ||
|
|
7b670fdd48 | ||
|
|
d6f2a7fb5c | ||
|
|
5b5ae16e92 |
@@ -110,11 +110,11 @@ check_lab5b() {
|
||||
check_cmd go test -c
|
||||
# also check other labs/parts
|
||||
cd "$tmpdir"
|
||||
check_lab4a
|
||||
check_lab5a
|
||||
cd "$tmpdir"
|
||||
check_lab4
|
||||
cd "$tmpdir"
|
||||
check_lab3
|
||||
cd "$tmpdir"
|
||||
check_lab2
|
||||
}
|
||||
|
||||
check_cmd() {
|
||||
|
||||
@@ -29,7 +29,7 @@ func MakeClerk(servers []*labrpc.ClientEnd) *Clerk {
|
||||
// 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)
|
||||
// ok := ck.servers[i].Call("KVServer."+op, &args, &reply)
|
||||
//
|
||||
// the types of args and reply (including whether they are pointers)
|
||||
// must match the declared types of the RPC handler function's
|
||||
|
||||
@@ -12,7 +12,6 @@ type Err string
|
||||
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.
|
||||
|
||||
@@ -42,7 +42,11 @@ func (kv *KVServer) Get(args *GetArgs, reply *GetReply) {
|
||||
// Your code here.
|
||||
}
|
||||
|
||||
func (kv *KVServer) PutAppend(args *PutAppendArgs, reply *PutAppendReply) {
|
||||
func (kv *KVServer) Put(args *PutAppendArgs, reply *PutAppendReply) {
|
||||
// Your code here.
|
||||
}
|
||||
|
||||
func (kv *KVServer) Append(args *PutAppendArgs, reply *PutAppendReply) {
|
||||
// Your code here.
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package kvsrv
|
||||
|
||||
import "6.5840/labrpc"
|
||||
import "crypto/rand"
|
||||
import "math/big"
|
||||
import (
|
||||
"crypto/rand"
|
||||
"log"
|
||||
"math/big"
|
||||
|
||||
"6.5840/labrpc"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
type Clerk struct {
|
||||
server *labrpc.ClientEnd
|
||||
@@ -35,9 +39,21 @@ func MakeClerk(server *labrpc.ClientEnd) *Clerk {
|
||||
// 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 {
|
||||
|
||||
id := uuid.New()
|
||||
args := GetArgs{
|
||||
Key: key,
|
||||
Uuid: id,
|
||||
}
|
||||
reply := GetReply{}
|
||||
ok := false
|
||||
for !ok {
|
||||
ok = ck.server.Call("KVServer.Get", &args, &reply)
|
||||
if !ok {
|
||||
log.Println("RPC call failed: GET op")
|
||||
}
|
||||
}
|
||||
// You will have to modify this function.
|
||||
return ""
|
||||
return reply.Value
|
||||
}
|
||||
|
||||
// shared by Put and Append.
|
||||
@@ -50,7 +66,22 @@ func (ck *Clerk) Get(key string) string {
|
||||
// arguments. and reply must be passed as a pointer.
|
||||
func (ck *Clerk) PutAppend(key string, value string, op string) string {
|
||||
// You will have to modify this function.
|
||||
return ""
|
||||
id := uuid.New()
|
||||
args := PutAppendArgs{
|
||||
Key: key,
|
||||
Value: value,
|
||||
Uuid: id,
|
||||
}
|
||||
reply := PutAppendReply{}
|
||||
ok := false
|
||||
for !ok {
|
||||
ok := ck.server.Call("KVServer."+op, &args, &reply)
|
||||
if !ok {
|
||||
log.Printf("RPC call failed: %s op\n", op)
|
||||
}
|
||||
}
|
||||
|
||||
return reply.Value
|
||||
}
|
||||
|
||||
func (ck *Clerk) Put(key string, value string) {
|
||||
|
||||
@@ -7,6 +7,7 @@ type PutAppendArgs struct {
|
||||
// You'll have to add definitions here.
|
||||
// Field names must start with capital letters,
|
||||
// otherwise RPC will break.
|
||||
Uuid string
|
||||
}
|
||||
|
||||
type PutAppendReply struct {
|
||||
@@ -16,6 +17,7 @@ type PutAppendReply struct {
|
||||
type GetArgs struct {
|
||||
Key string
|
||||
// You'll have to add definitions here.
|
||||
Uuid string
|
||||
}
|
||||
|
||||
type GetReply struct {
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
const Debug = false
|
||||
const Debug = true
|
||||
|
||||
func DPrintf(format string, a ...interface{}) (n int, err error) {
|
||||
if Debug {
|
||||
@@ -14,30 +14,47 @@ func DPrintf(format string, a ...interface{}) (n int, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
type KVServer struct {
|
||||
mu sync.Mutex
|
||||
|
||||
KVStore map[string]string
|
||||
OpLog map[string]bool
|
||||
// Your definitions here.
|
||||
}
|
||||
|
||||
|
||||
func (kv *KVServer) Get(args *GetArgs, reply *GetReply) {
|
||||
// Your code here.
|
||||
kv.mu.Lock()
|
||||
defer kv.mu.Unlock()
|
||||
|
||||
reply.Value = kv.KVStore[args.Key]
|
||||
}
|
||||
|
||||
func (kv *KVServer) Put(args *PutAppendArgs, reply *PutAppendReply) {
|
||||
// Your code here.
|
||||
kv.mu.Lock()
|
||||
defer kv.mu.Unlock()
|
||||
|
||||
kv.KVStore[args.Key] = args.Value
|
||||
reply.Value = args.Value
|
||||
}
|
||||
|
||||
func (kv *KVServer) Append(args *PutAppendArgs, reply *PutAppendReply) {
|
||||
// Your code here.
|
||||
kv.mu.Lock()
|
||||
defer kv.mu.Unlock()
|
||||
|
||||
val, exists := kv.KVStore[args.Key]
|
||||
if !exists {
|
||||
kv.KVStore[args.Key] = args.Value
|
||||
reply.Value = ""
|
||||
} else {
|
||||
kv.KVStore[args.Key] = val + args.Value
|
||||
reply.Value = val
|
||||
}
|
||||
}
|
||||
|
||||
func StartKVServer() *KVServer {
|
||||
kv := new(KVServer)
|
||||
|
||||
// You may need initialization code here.
|
||||
|
||||
kv.KVStore = map[string]string{}
|
||||
return kv
|
||||
}
|
||||
|
||||
@@ -373,7 +373,7 @@ const (
|
||||
)
|
||||
|
||||
func TestMemGet2(t *testing.T) {
|
||||
const MEM = 100 // in MiB
|
||||
const MEM = 10 // in MiB
|
||||
|
||||
cfg := make_config(t, true)
|
||||
defer cfg.cleanup()
|
||||
@@ -408,7 +408,7 @@ func TestMemGet2(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMemPut2(t *testing.T) {
|
||||
const MEM = 100 // in MiB
|
||||
const MEM = 10 // in MiB
|
||||
|
||||
cfg := make_config(t, false)
|
||||
defer cfg.cleanup()
|
||||
@@ -423,6 +423,7 @@ func TestMemPut2(t *testing.T) {
|
||||
ck1.Put("k", "")
|
||||
|
||||
runtime.GC()
|
||||
|
||||
var st runtime.MemStats
|
||||
runtime.ReadMemStats(&st)
|
||||
m := st.HeapAlloc / MiB
|
||||
@@ -433,7 +434,7 @@ func TestMemPut2(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestMemAppend2(t *testing.T) {
|
||||
const MEM = 100 // in MiB
|
||||
const MEM = 10 // in MiB
|
||||
|
||||
cfg := make_config(t, false)
|
||||
defer cfg.cleanup()
|
||||
@@ -458,63 +459,26 @@ func TestMemAppend2(t *testing.T) {
|
||||
cfg.end()
|
||||
}
|
||||
|
||||
func TestMemPutMany2(t *testing.T) {
|
||||
func TestMemPutManyClients(t *testing.T) {
|
||||
const (
|
||||
NPUT = 1_000_000
|
||||
NCLIENT = 100_000
|
||||
MEM = 1000
|
||||
)
|
||||
|
||||
cfg := make_config(t, false)
|
||||
defer cfg.cleanup()
|
||||
|
||||
cfg.begin("Test: memory use many puts")
|
||||
ck := cfg.makeClient()
|
||||
|
||||
v := randValue(MEM)
|
||||
|
||||
ck.Put("k", v)
|
||||
|
||||
// allow threads started by labrpc to exit
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
runtime.GC()
|
||||
|
||||
var st runtime.MemStats
|
||||
runtime.ReadMemStats(&st)
|
||||
m0 := st.HeapAlloc
|
||||
|
||||
for i := 0; i < NPUT; i++ {
|
||||
ck.Put("k", v)
|
||||
cks := make([]*Clerk, NCLIENT)
|
||||
for i, _ := range cks {
|
||||
cks[i] = cfg.makeClient()
|
||||
}
|
||||
|
||||
// allow threads started by labrpc to exit
|
||||
// allow threads started by labrpc to start
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
runtime.GC()
|
||||
runtime.ReadMemStats(&st)
|
||||
m1 := st.HeapAlloc
|
||||
|
||||
//log.Printf("mem m0 %d m1 %d\n", m0, m1)
|
||||
|
||||
if m1 > m0+NPUT/10 {
|
||||
t.Fatalf("error: server using too much memory %d %d\n", m0, m1)
|
||||
}
|
||||
cfg.end()
|
||||
}
|
||||
|
||||
func TestMemGetMany2(t *testing.T) {
|
||||
const (
|
||||
NCLIENT = 100_000
|
||||
)
|
||||
|
||||
cfg := make_config(t, false)
|
||||
defer cfg.cleanup()
|
||||
|
||||
cfg.begin("Test: memory use many gets")
|
||||
|
||||
ck := cfg.makeClient()
|
||||
ck.Put("0", "")
|
||||
cfg.deleteClient(ck)
|
||||
cfg.begin("Test: memory use many put clients")
|
||||
|
||||
runtime.GC()
|
||||
runtime.GC()
|
||||
@@ -524,25 +488,119 @@ func TestMemGetMany2(t *testing.T) {
|
||||
m0 := st.HeapAlloc
|
||||
|
||||
for i := 0; i < NCLIENT; i++ {
|
||||
ck := cfg.makeClient()
|
||||
ck.Get("0")
|
||||
cfg.deleteClient(ck)
|
||||
cks[i].Put("k", v)
|
||||
}
|
||||
|
||||
runtime.GC()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
runtime.GC()
|
||||
runtime.GC()
|
||||
//runtime.GC()
|
||||
|
||||
runtime.ReadMemStats(&st)
|
||||
m1 := st.HeapAlloc
|
||||
f := (float64(m1) - float64(m0)) / NCLIENT
|
||||
if m1 > m0+(NCLIENT*200) {
|
||||
t.Fatalf("error: server using too much memory %d %d (%.2f per client)\n", m0, m1, f)
|
||||
}
|
||||
|
||||
log.Printf("mem m0 %d m1 %d\n", m0, m1)
|
||||
|
||||
if m1 >= m0+NCLIENT {
|
||||
t.Fatalf("error: server using too much memory m0 %d m1 %d\n", m0, m1)
|
||||
for _, ck := range cks {
|
||||
cfg.deleteClient(ck)
|
||||
}
|
||||
|
||||
cfg.end()
|
||||
}
|
||||
|
||||
func TestMemGetManyClients(t *testing.T) {
|
||||
const (
|
||||
NCLIENT = 100_000
|
||||
)
|
||||
|
||||
cfg := make_config(t, false)
|
||||
defer cfg.cleanup()
|
||||
|
||||
cfg.begin("Test: memory use many get client")
|
||||
|
||||
ck := cfg.makeClient()
|
||||
ck.Put("0", "")
|
||||
cfg.deleteClient(ck)
|
||||
|
||||
cks := make([]*Clerk, NCLIENT)
|
||||
for i, _ := range cks {
|
||||
cks[i] = cfg.makeClient()
|
||||
}
|
||||
|
||||
// allow threads started by labrpc to start
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
runtime.GC()
|
||||
runtime.GC()
|
||||
|
||||
var st runtime.MemStats
|
||||
runtime.ReadMemStats(&st)
|
||||
m0 := st.HeapAlloc
|
||||
|
||||
for i := 0; i < NCLIENT; i++ {
|
||||
cks[i].Get("0")
|
||||
}
|
||||
|
||||
runtime.GC()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
runtime.GC()
|
||||
|
||||
runtime.ReadMemStats(&st)
|
||||
m1 := st.HeapAlloc
|
||||
f := (float64(m1) - float64(m0)) / NCLIENT
|
||||
if m1 >= m0+NCLIENT*10 {
|
||||
t.Fatalf("error: server using too much memory m0 %d m1 %d (%.2f per client)\n", m0, m1, f)
|
||||
}
|
||||
|
||||
for _, ck := range cks {
|
||||
cfg.deleteClient(ck)
|
||||
}
|
||||
|
||||
cfg.end()
|
||||
}
|
||||
|
||||
func TestMemManyAppends(t *testing.T) {
|
||||
const (
|
||||
N = 1000
|
||||
MEM = 1000
|
||||
)
|
||||
|
||||
cfg := make_config(t, false)
|
||||
defer cfg.cleanup()
|
||||
|
||||
cfg.begin("Test: memory use many appends")
|
||||
|
||||
ck := cfg.makeClient()
|
||||
rdVal := randValue(MEM)
|
||||
|
||||
runtime.GC()
|
||||
runtime.GC()
|
||||
|
||||
var st runtime.MemStats
|
||||
runtime.ReadMemStats(&st)
|
||||
m0 := st.HeapAlloc
|
||||
|
||||
for i := 0; i < N; i++ {
|
||||
ck.Append("k", rdVal)
|
||||
}
|
||||
|
||||
runtime.GC()
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
runtime.GC()
|
||||
|
||||
runtime.ReadMemStats(&st)
|
||||
m1 := st.HeapAlloc
|
||||
if m1 >= 3*MEM*N {
|
||||
t.Fatalf("error: server using too much memory m0 %d m1 %d\n", m0, m1)
|
||||
}
|
||||
|
||||
log.Printf("m0 %d m1 %d\n", m0, m1)
|
||||
|
||||
cfg.deleteClient(ck)
|
||||
cfg.end()
|
||||
}
|
||||
|
||||
@@ -551,7 +551,7 @@ func (cfg *config) wait(index int, n int, startTerm int) interface{} {
|
||||
// if retry==true, may submit the command multiple
|
||||
// times, in case a leader fails just after Start().
|
||||
// if retry==false, calls Start() only once, in order
|
||||
// to simplify the early Lab 2B tests.
|
||||
// to simplify the early Lab 3B tests.
|
||||
func (cfg *config) one(cmd interface{}, expectedServers int, retry bool) int {
|
||||
t0 := time.Now()
|
||||
starts := 0
|
||||
@@ -605,7 +605,7 @@ func (cfg *config) one(cmd interface{}, expectedServers int, retry bool) int {
|
||||
|
||||
// start a Test.
|
||||
// print the Test message.
|
||||
// e.g. cfg.begin("Test (2B): RPC counts aren't too high")
|
||||
// e.g. cfg.begin("Test (3B): RPC counts aren't too high")
|
||||
func (cfg *config) begin(description string) {
|
||||
fmt.Printf("%s ...\n", description)
|
||||
cfg.t0 = time.Now()
|
||||
|
||||
@@ -35,7 +35,7 @@ import (
|
||||
// CommandValid to true to indicate that the ApplyMsg contains a newly
|
||||
// committed log entry.
|
||||
//
|
||||
// in part 2D you'll want to send other kinds of messages (e.g.,
|
||||
// in part 3D you'll want to send other kinds of messages (e.g.,
|
||||
// snapshots) on the applyCh, but set CommandValid to false for these
|
||||
// other uses.
|
||||
type ApplyMsg struct {
|
||||
@@ -43,7 +43,7 @@ type ApplyMsg struct {
|
||||
Command interface{}
|
||||
CommandIndex int
|
||||
|
||||
// For 2D:
|
||||
// For 3D:
|
||||
SnapshotValid bool
|
||||
Snapshot []byte
|
||||
SnapshotTerm int
|
||||
@@ -58,7 +58,7 @@ type Raft struct {
|
||||
me int // this peer's index into peers[]
|
||||
dead int32 // set by Kill()
|
||||
|
||||
// Your data here (2A, 2B, 2C).
|
||||
// Your data here (3A, 3B, 3C).
|
||||
// Look at the paper's Figure 2 for a description of what
|
||||
// state a Raft server must maintain.
|
||||
|
||||
@@ -70,7 +70,7 @@ func (rf *Raft) GetState() (int, bool) {
|
||||
|
||||
var term int
|
||||
var isleader bool
|
||||
// Your code here (2A).
|
||||
// Your code here (3A).
|
||||
return term, isleader
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ func (rf *Raft) GetState() (int, bool) {
|
||||
// after you've implemented snapshots, pass the current snapshot
|
||||
// (or nil if there's not yet a snapshot).
|
||||
func (rf *Raft) persist() {
|
||||
// Your code here (2C).
|
||||
// Your code here (3C).
|
||||
// Example:
|
||||
// w := new(bytes.Buffer)
|
||||
// e := labgob.NewEncoder(w)
|
||||
@@ -98,7 +98,7 @@ func (rf *Raft) readPersist(data []byte) {
|
||||
if data == nil || len(data) < 1 { // bootstrap without any state?
|
||||
return
|
||||
}
|
||||
// Your code here (2C).
|
||||
// Your code here (3C).
|
||||
// Example:
|
||||
// r := bytes.NewBuffer(data)
|
||||
// d := labgob.NewDecoder(r)
|
||||
@@ -119,7 +119,7 @@ func (rf *Raft) readPersist(data []byte) {
|
||||
// service no longer needs the log through (and including)
|
||||
// that index. Raft should now trim its log as much as possible.
|
||||
func (rf *Raft) Snapshot(index int, snapshot []byte) {
|
||||
// Your code here (2D).
|
||||
// Your code here (3D).
|
||||
|
||||
}
|
||||
|
||||
@@ -127,18 +127,18 @@ func (rf *Raft) Snapshot(index int, snapshot []byte) {
|
||||
// example RequestVote RPC arguments structure.
|
||||
// field names must start with capital letters!
|
||||
type RequestVoteArgs struct {
|
||||
// Your data here (2A, 2B).
|
||||
// Your data here (3A, 3B).
|
||||
}
|
||||
|
||||
// example RequestVote RPC reply structure.
|
||||
// field names must start with capital letters!
|
||||
type RequestVoteReply struct {
|
||||
// Your data here (2A).
|
||||
// Your data here (3A).
|
||||
}
|
||||
|
||||
// example RequestVote RPC handler.
|
||||
func (rf *Raft) RequestVote(args *RequestVoteArgs, reply *RequestVoteReply) {
|
||||
// Your code here (2A, 2B).
|
||||
// Your code here (3A, 3B).
|
||||
}
|
||||
|
||||
// example code to send a RequestVote RPC to a server.
|
||||
@@ -191,7 +191,7 @@ func (rf *Raft) Start(command interface{}) (int, int, bool) {
|
||||
term := -1
|
||||
isLeader := true
|
||||
|
||||
// Your code here (2B).
|
||||
// Your code here (3B).
|
||||
|
||||
|
||||
return index, term, isLeader
|
||||
@@ -219,7 +219,7 @@ func (rf *Raft) killed() bool {
|
||||
func (rf *Raft) ticker() {
|
||||
for rf.killed() == false {
|
||||
|
||||
// Your code here (2A)
|
||||
// Your code here (3A)
|
||||
// Check if a leader election should be started.
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ func Make(peers []*labrpc.ClientEnd, me int,
|
||||
rf.persister = persister
|
||||
rf.me = me
|
||||
|
||||
// Your initialization code here (2A, 2B, 2C).
|
||||
// Your initialization code here (3A, 3B, 3C).
|
||||
|
||||
// initialize from state persisted before a crash
|
||||
rf.readPersist(persister.ReadRaftState())
|
||||
|
||||
Reference in New Issue
Block a user