├── 6.824-2015 ├── .gitignore ├── Makefile └── src │ ├── .gitignore │ ├── diskv │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go │ ├── kvpaxos │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go │ ├── lockservice │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go │ ├── main │ ├── diskvd.go │ ├── kjv12.txt │ ├── lockc.go │ ├── lockd.go │ ├── mr-testout.txt │ ├── pbc.go │ ├── pbd.go │ ├── test-wc.sh │ ├── viewd.go │ └── wc.go │ ├── mapreduce │ ├── common.go │ ├── mapreduce.go │ ├── master.go │ ├── test_test.go │ └── worker.go │ ├── paxos │ ├── paxos.go │ └── test_test.go │ ├── pbservice │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go │ ├── shardkv │ ├── client.go │ ├── common.go │ ├── savetest │ ├── server.go │ └── test_test.go │ ├── shardmaster │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go │ └── viewservice │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go ├── 6.824-2016 ├── .gitignore ├── Makefile └── src │ ├── .gitignore │ ├── diskv │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go │ ├── kvpaxos │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go │ ├── kvraft │ ├── client.go │ ├── common.go │ ├── config.go │ ├── server.go │ └── test_test.go │ ├── labrpc │ ├── labrpc.go │ └── test_test.go │ ├── lockservice │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go │ ├── main │ ├── diskvd │ ├── diskvd.go │ ├── ii.go │ ├── lockc.go │ ├── lockd.go │ ├── mr-challenge.txt │ ├── mr-testout.txt │ ├── pbc.go │ ├── pbd.go │ ├── pg-being_ernest.txt │ ├── pg-dorian_gray.txt │ ├── pg-dracula.txt │ ├── pg-emma.txt │ ├── pg-frankenstein.txt │ ├── pg-great_expectations.txt │ ├── pg-grimm.txt │ ├── pg-huckleberry_finn.txt │ ├── pg-les_miserables.txt │ ├── pg-metamorphosis.txt │ ├── pg-moby_dick.txt │ ├── pg-sherlock_holmes.txt │ ├── pg-tale_of_two_cities.txt │ ├── pg-tom_sawyer.txt │ ├── pg-ulysses.txt │ ├── pg-war_and_peace.txt │ ├── test-ii.sh │ ├── test-mr.sh │ ├── test-wc.sh │ ├── viewd.go │ └── wc.go │ ├── mapreduce │ ├── common.go │ ├── common_map.go │ ├── common_reduce.go │ ├── common_rpc.go │ ├── master.go │ ├── master_rpc.go │ ├── master_splitmerge.go │ ├── readme.go │ ├── schedule.go │ ├── test_test.go │ └── worker.go │ ├── paxos-shardkv │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go │ ├── paxos-shardmaster │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go │ ├── paxos │ ├── paxos.go │ └── test_test.go │ ├── pbservice │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go │ ├── raft │ ├── config.go │ ├── persister.go │ ├── raft.go │ ├── test_test.go │ └── util.go │ ├── shardkv │ ├── client.go │ ├── common.go │ ├── config.go │ ├── server.go │ └── test_test.go │ ├── shardmaster │ ├── client.go │ ├── common.go │ ├── config.go │ ├── server.go │ └── test_test.go │ └── viewservice │ ├── client.go │ ├── common.go │ ├── server.go │ └── test_test.go ├── LICENSE └── README.md /6.824-2015/.gitignore: -------------------------------------------------------------------------------- 1 | api.key 2 | *-handin.tar.gz -------------------------------------------------------------------------------- /6.824-2015/Makefile: -------------------------------------------------------------------------------- 1 | # This is the Makefile helping you submit the labs. 2 | # Just create 6.824/api.key with your API key in it, 3 | # and submit your lab with the following command: 4 | # $ make [lab1|lab2a|lab2b|lab3a|lab3b|lab4a|lab4b|lab5] 5 | 6 | KEY=$(shell cat api.key) 7 | LABS=" lab1 lab2a lab2b lab3a lab3b lab4a lab4b lab5 " 8 | 9 | %: 10 | @if echo $(LABS) | grep -q " $@ " ; then \ 11 | tar cvzf $@-handin.tar.gz --exclude=src/main/kjv12.txt Makefile .git src; \ 12 | if test -z $(KEY) ; then \ 13 | echo "Missing $(PWD)/api.key. Please create the file with your key in it or submit the $@-handin.tar.gz via the web interface."; \ 14 | else \ 15 | echo "Are you sure you want to submit $@? Enter 'yes' to continue:"; \ 16 | read line; \ 17 | if test $$line != "yes" ; then echo "Giving up submission"; exit; fi; \ 18 | if test `stat -c "%s" "$@-handin.tar.gz" 2>/dev/null || stat -f "%z" "$@-handin.tar.gz"` -ge 20971520 ; then echo "File exceeds 20MB."; exit; fi; \ 19 | curl -F file=@$@-handin.tar.gz -F key=$(KEY) http://6824.scripts.mit.edu/submit/handin.py/upload; \ 20 | fi; \ 21 | else \ 22 | echo "Bad target $@. Usage: make [$(LABS)]"; \ 23 | fi 24 | -------------------------------------------------------------------------------- /6.824-2015/src/.gitignore: -------------------------------------------------------------------------------- 1 | mrtmp.* 2 | /main/diff.out 3 | /mapreduce/x.txt 4 | /pbservice/x.txt 5 | /kvpaxos/x.txt 6 | -------------------------------------------------------------------------------- /6.824-2015/src/diskv/client.go: -------------------------------------------------------------------------------- 1 | package diskv 2 | 3 | import "shardmaster" 4 | import "net/rpc" 5 | import "time" 6 | import "sync" 7 | import "fmt" 8 | import "crypto/rand" 9 | import "math/big" 10 | 11 | type Clerk struct { 12 | mu sync.Mutex // one RPC at a time 13 | sm *shardmaster.Clerk 14 | config shardmaster.Config 15 | // You'll have to modify Clerk. 16 | } 17 | 18 | func nrand() int64 { 19 | max := big.NewInt(int64(1) << 62) 20 | bigx, _ := rand.Int(rand.Reader, max) 21 | x := bigx.Int64() 22 | return x 23 | } 24 | 25 | func MakeClerk(shardmasters []string) *Clerk { 26 | ck := new(Clerk) 27 | ck.sm = shardmaster.MakeClerk(shardmasters) 28 | // You'll have to modify MakeClerk. 29 | return ck 30 | } 31 | 32 | // 33 | // call() sends an RPC to the rpcname handler on server srv 34 | // with arguments args, waits for the reply, and leaves the 35 | // reply in reply. the reply argument should be a pointer 36 | // to a reply structure. 37 | // 38 | // the return value is true if the server responded, and false 39 | // if call() was not able to contact the server. in particular, 40 | // the reply's contents are only valid if call() returned true. 41 | // 42 | // you should assume that call() will return an 43 | // error after a while if the server is dead. 44 | // don't provide your own time-out mechanism. 45 | // 46 | // please use call() to send all RPCs, in client.go and server.go. 47 | // please don't change this function. 48 | // 49 | func call(srv string, rpcname string, 50 | args interface{}, reply interface{}) bool { 51 | c, errx := rpc.Dial("unix", srv) 52 | if errx != nil { 53 | return false 54 | } 55 | defer c.Close() 56 | 57 | err := c.Call(rpcname, args, reply) 58 | if err == nil { 59 | return true 60 | } 61 | 62 | fmt.Println(err) 63 | return false 64 | } 65 | 66 | // 67 | // which shard is a key in? 68 | // please use this function, 69 | // and please do not change it. 70 | // 71 | func key2shard(key string) int { 72 | shard := 0 73 | if len(key) > 0 { 74 | shard = int(key[0]) 75 | } 76 | shard %= shardmaster.NShards 77 | return shard 78 | } 79 | 80 | // 81 | // fetch the current value for a key. 82 | // returns "" if the key does not exist. 83 | // keeps trying forever in the face of all other errors. 84 | // 85 | func (ck *Clerk) Get(key string) string { 86 | ck.mu.Lock() 87 | defer ck.mu.Unlock() 88 | 89 | // You'll have to modify Get(). 90 | 91 | for { 92 | shard := key2shard(key) 93 | 94 | gid := ck.config.Shards[shard] 95 | 96 | servers, ok := ck.config.Groups[gid] 97 | 98 | if ok { 99 | // try each server in the shard's replication group. 100 | for _, srv := range servers { 101 | args := &GetArgs{} 102 | args.Key = key 103 | var reply GetReply 104 | ok := call(srv, "DisKV.Get", args, &reply) 105 | if ok && (reply.Err == OK || reply.Err == ErrNoKey) { 106 | return reply.Value 107 | } 108 | if ok && (reply.Err == ErrWrongGroup) { 109 | break 110 | } 111 | } 112 | } 113 | 114 | time.Sleep(100 * time.Millisecond) 115 | 116 | // ask master for a new configuration. 117 | ck.config = ck.sm.Query(-1) 118 | } 119 | } 120 | 121 | // send a Put or Append request. 122 | func (ck *Clerk) PutAppend(key string, value string, op string) { 123 | ck.mu.Lock() 124 | defer ck.mu.Unlock() 125 | 126 | // You'll have to modify PutAppend(). 127 | 128 | for { 129 | shard := key2shard(key) 130 | 131 | gid := ck.config.Shards[shard] 132 | 133 | servers, ok := ck.config.Groups[gid] 134 | 135 | if ok { 136 | // try each server in the shard's replication group. 137 | for _, srv := range servers { 138 | args := &PutAppendArgs{} 139 | args.Key = key 140 | args.Value = value 141 | args.Op = op 142 | var reply PutAppendReply 143 | ok := call(srv, "DisKV.PutAppend", args, &reply) 144 | if ok && reply.Err == OK { 145 | return 146 | } 147 | if ok && (reply.Err == ErrWrongGroup) { 148 | break 149 | } 150 | } 151 | } 152 | 153 | time.Sleep(100 * time.Millisecond) 154 | 155 | // ask master for a new configuration. 156 | ck.config = ck.sm.Query(-1) 157 | } 158 | } 159 | 160 | func (ck *Clerk) Put(key string, value string) { 161 | ck.PutAppend(key, value, "Put") 162 | } 163 | func (ck *Clerk) Append(key string, value string) { 164 | ck.PutAppend(key, value, "Append") 165 | } 166 | -------------------------------------------------------------------------------- /6.824-2015/src/diskv/common.go: -------------------------------------------------------------------------------- 1 | package diskv 2 | 3 | // 4 | // Sharded key/value server. 5 | // Lots of replica groups, each running op-at-a-time paxos. 6 | // Shardmaster decides which group serves each shard. 7 | // Shardmaster may change shard assignment from time to time. 8 | // 9 | // You will have to modify these definitions. 10 | // 11 | 12 | const ( 13 | OK = "OK" 14 | ErrNoKey = "ErrNoKey" 15 | ErrWrongGroup = "ErrWrongGroup" 16 | ) 17 | 18 | type Err string 19 | 20 | type PutAppendArgs struct { 21 | Key string 22 | Value string 23 | Op string // "Put" or "Append" 24 | // You'll have to add definitions here. 25 | // Field names must start with capital letters, 26 | // otherwise RPC will break. 27 | 28 | } 29 | 30 | type PutAppendReply struct { 31 | Err Err 32 | } 33 | 34 | type GetArgs struct { 35 | Key string 36 | // You'll have to add definitions here. 37 | } 38 | 39 | type GetReply struct { 40 | Err Err 41 | Value string 42 | } 43 | -------------------------------------------------------------------------------- /6.824-2015/src/diskv/server.go: -------------------------------------------------------------------------------- 1 | package diskv 2 | 3 | import "net" 4 | import "fmt" 5 | import "net/rpc" 6 | import "log" 7 | import "time" 8 | import "paxos" 9 | import "sync" 10 | import "sync/atomic" 11 | import "os" 12 | import "syscall" 13 | import "encoding/gob" 14 | import "encoding/base32" 15 | import "math/rand" 16 | import "shardmaster" 17 | import "io/ioutil" 18 | import "strconv" 19 | 20 | const Debug = 0 21 | 22 | func DPrintf(format string, a ...interface{}) (n int, err error) { 23 | if Debug > 0 { 24 | log.Printf(format, a...) 25 | } 26 | return 27 | } 28 | 29 | type Op struct { 30 | // Your definitions here. 31 | } 32 | 33 | type DisKV struct { 34 | mu sync.Mutex 35 | l net.Listener 36 | me int 37 | dead int32 // for testing 38 | unreliable int32 // for testing 39 | sm *shardmaster.Clerk 40 | px *paxos.Paxos 41 | dir string // each replica has its own data directory 42 | 43 | gid int64 // my replica group ID 44 | 45 | // Your definitions here. 46 | } 47 | 48 | // 49 | // these are handy functions that might be useful 50 | // for reading and writing key/value files, and 51 | // for reading and writing entire shards. 52 | // puts the key files for each shard in a separate 53 | // directory. 54 | // 55 | 56 | func (kv *DisKV) shardDir(shard int) string { 57 | d := kv.dir + "/shard-" + strconv.Itoa(shard) + "/" 58 | // create directory if needed. 59 | _, err := os.Stat(d) 60 | if err != nil { 61 | if err := os.Mkdir(d, 0777); err != nil { 62 | log.Fatalf("Mkdir(%v): %v", d, err) 63 | } 64 | } 65 | return d 66 | } 67 | 68 | // cannot use keys in file names directly, since 69 | // they might contain troublesome characters like /. 70 | // base32-encode the key to get a file name. 71 | // base32 rather than base64 b/c Mac has case-insensitive 72 | // file names. 73 | func (kv *DisKV) encodeKey(key string) string { 74 | return base32.StdEncoding.EncodeToString([]byte(key)) 75 | } 76 | 77 | func (kv *DisKV) decodeKey(filename string) (string, error) { 78 | key, err := base32.StdEncoding.DecodeString(filename) 79 | return string(key), err 80 | } 81 | 82 | // read the content of a key's file. 83 | func (kv *DisKV) fileGet(shard int, key string) (string, error) { 84 | fullname := kv.shardDir(shard) + "/key-" + kv.encodeKey(key) 85 | content, err := ioutil.ReadFile(fullname) 86 | return string(content), err 87 | } 88 | 89 | // replace the content of a key's file. 90 | // uses rename() to make the replacement atomic with 91 | // respect to crashes. 92 | func (kv *DisKV) filePut(shard int, key string, content string) error { 93 | fullname := kv.shardDir(shard) + "/key-" + kv.encodeKey(key) 94 | tempname := kv.shardDir(shard) + "/temp-" + kv.encodeKey(key) 95 | if err := ioutil.WriteFile(tempname, []byte(content), 0666); err != nil { 96 | return err 97 | } 98 | if err := os.Rename(tempname, fullname); err != nil { 99 | return err 100 | } 101 | return nil 102 | } 103 | 104 | // return content of every key file in a given shard. 105 | func (kv *DisKV) fileReadShard(shard int) map[string]string { 106 | m := map[string]string{} 107 | d := kv.shardDir(shard) 108 | files, err := ioutil.ReadDir(d) 109 | if err != nil { 110 | log.Fatalf("fileReadShard could not read %v: %v", d, err) 111 | } 112 | for _, fi := range files { 113 | n1 := fi.Name() 114 | if n1[0:4] == "key-" { 115 | key, err := kv.decodeKey(n1[4:]) 116 | if err != nil { 117 | log.Fatalf("fileReadShard bad file name %v: %v", n1, err) 118 | } 119 | content, err := kv.fileGet(shard, key) 120 | if err != nil { 121 | log.Fatalf("fileReadShard fileGet failed for %v: %v", key, err) 122 | } 123 | m[key] = content 124 | } 125 | } 126 | return m 127 | } 128 | 129 | // replace an entire shard directory. 130 | func (kv *DisKV) fileReplaceShard(shard int, m map[string]string) { 131 | d := kv.shardDir(shard) 132 | os.RemoveAll(d) // remove all existing files from shard. 133 | for k, v := range m { 134 | kv.filePut(shard, k, v) 135 | } 136 | } 137 | 138 | func (kv *DisKV) Get(args *GetArgs, reply *GetReply) error { 139 | // Your code here. 140 | return nil 141 | } 142 | 143 | // RPC handler for client Put and Append requests 144 | func (kv *DisKV) PutAppend(args *PutAppendArgs, reply *PutAppendReply) error { 145 | // Your code here. 146 | return nil 147 | } 148 | 149 | // 150 | // Ask the shardmaster if there's a new configuration; 151 | // if so, re-configure. 152 | // 153 | func (kv *DisKV) tick() { 154 | // Your code here. 155 | } 156 | 157 | // tell the server to shut itself down. 158 | // please don't change these two functions. 159 | func (kv *DisKV) kill() { 160 | atomic.StoreInt32(&kv.dead, 1) 161 | kv.l.Close() 162 | kv.px.Kill() 163 | } 164 | 165 | // call this to find out if the server is dead. 166 | func (kv *DisKV) isdead() bool { 167 | return atomic.LoadInt32(&kv.dead) != 0 168 | } 169 | 170 | // please do not change these two functions. 171 | func (kv *DisKV) Setunreliable(what bool) { 172 | if what { 173 | atomic.StoreInt32(&kv.unreliable, 1) 174 | } else { 175 | atomic.StoreInt32(&kv.unreliable, 0) 176 | } 177 | } 178 | 179 | func (kv *DisKV) isunreliable() bool { 180 | return atomic.LoadInt32(&kv.unreliable) != 0 181 | } 182 | 183 | // 184 | // Start a shardkv server. 185 | // gid is the ID of the server's replica group. 186 | // shardmasters[] contains the ports of the 187 | // servers that implement the shardmaster. 188 | // servers[] contains the ports of the servers 189 | // in this replica group. 190 | // Me is the index of this server in servers[]. 191 | // dir is the directory name under which this 192 | // replica should store all its files. 193 | // each replica is passed a different directory. 194 | // restart is false the very first time this server 195 | // is started, and true to indicate a re-start 196 | // after a crash or after a crash with disk loss. 197 | // 198 | func StartServer(gid int64, shardmasters []string, 199 | servers []string, me int, dir string, restart bool) *DisKV { 200 | 201 | kv := new(DisKV) 202 | kv.me = me 203 | kv.gid = gid 204 | kv.sm = shardmaster.MakeClerk(shardmasters) 205 | kv.dir = dir 206 | 207 | // Your initialization code here. 208 | // Don't call Join(). 209 | 210 | // log.SetOutput(ioutil.Discard) 211 | 212 | gob.Register(Op{}) 213 | 214 | rpcs := rpc.NewServer() 215 | rpcs.Register(kv) 216 | 217 | kv.px = paxos.Make(servers, me, rpcs) 218 | 219 | // log.SetOutput(os.Stdout) 220 | 221 | os.Remove(servers[me]) 222 | l, e := net.Listen("unix", servers[me]) 223 | if e != nil { 224 | log.Fatal("listen error: ", e) 225 | } 226 | kv.l = l 227 | 228 | // please do not change any of the following code, 229 | // or do anything to subvert it. 230 | 231 | go func() { 232 | for kv.isdead() == false { 233 | conn, err := kv.l.Accept() 234 | if err == nil && kv.isdead() == false { 235 | if kv.isunreliable() && (rand.Int63()%1000) < 100 { 236 | // discard the request. 237 | conn.Close() 238 | } else if kv.isunreliable() && (rand.Int63()%1000) < 200 { 239 | // process the request but force discard of reply. 240 | c1 := conn.(*net.UnixConn) 241 | f, _ := c1.File() 242 | err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR) 243 | if err != nil { 244 | fmt.Printf("shutdown: %v\n", err) 245 | } 246 | go rpcs.ServeConn(conn) 247 | } else { 248 | go rpcs.ServeConn(conn) 249 | } 250 | } else if err == nil { 251 | conn.Close() 252 | } 253 | if err != nil && kv.isdead() == false { 254 | fmt.Printf("DisKV(%v) accept: %v\n", me, err.Error()) 255 | kv.kill() 256 | } 257 | } 258 | }() 259 | 260 | go func() { 261 | for kv.isdead() == false { 262 | kv.tick() 263 | time.Sleep(250 * time.Millisecond) 264 | } 265 | }() 266 | 267 | return kv 268 | } 269 | -------------------------------------------------------------------------------- /6.824-2015/src/kvpaxos/client.go: -------------------------------------------------------------------------------- 1 | package kvpaxos 2 | 3 | import "net/rpc" 4 | import "crypto/rand" 5 | import "math/big" 6 | 7 | import "fmt" 8 | 9 | type Clerk struct { 10 | servers []string 11 | // You will have to modify this struct. 12 | } 13 | 14 | func nrand() int64 { 15 | max := big.NewInt(int64(1) << 62) 16 | bigx, _ := rand.Int(rand.Reader, max) 17 | x := bigx.Int64() 18 | return x 19 | } 20 | 21 | func MakeClerk(servers []string) *Clerk { 22 | ck := new(Clerk) 23 | ck.servers = servers 24 | // You'll have to add code here. 25 | return ck 26 | } 27 | 28 | // 29 | // call() sends an RPC to the rpcname handler on server srv 30 | // with arguments args, waits for the reply, and leaves the 31 | // reply in reply. the reply argument should be a pointer 32 | // to a reply structure. 33 | // 34 | // the return value is true if the server responded, and false 35 | // if call() was not able to contact the server. in particular, 36 | // the reply's contents are only valid if call() returned true. 37 | // 38 | // you should assume that call() will return an 39 | // error after a while if the server is dead. 40 | // don't provide your own time-out mechanism. 41 | // 42 | // please use call() to send all RPCs, in client.go and server.go. 43 | // please don't change this function. 44 | // 45 | func call(srv string, rpcname string, 46 | args interface{}, reply interface{}) bool { 47 | c, errx := rpc.Dial("unix", srv) 48 | if errx != nil { 49 | return false 50 | } 51 | defer c.Close() 52 | 53 | err := c.Call(rpcname, args, reply) 54 | if err == nil { 55 | return true 56 | } 57 | 58 | fmt.Println(err) 59 | return false 60 | } 61 | 62 | // 63 | // fetch the current value for a key. 64 | // returns "" if the key does not exist. 65 | // keeps trying forever in the face of all other errors. 66 | // 67 | func (ck *Clerk) Get(key string) string { 68 | // You will have to modify this function. 69 | id := nrand() 70 | i := 0 71 | for { 72 | args := &GetArgs{} 73 | args.Key = key 74 | args.Id = id 75 | var reply GetReply 76 | ok := call(ck.servers[i], "KVPaxos.Get", args, &reply) 77 | if !ok { 78 | i = (i + 1) % len(ck.servers) 79 | continue 80 | } 81 | 82 | if reply.Err == ErrNoKey { 83 | return "" 84 | } 85 | 86 | return reply.Value 87 | } 88 | } 89 | 90 | // 91 | // shared by Put and Append. 92 | // 93 | func (ck *Clerk) PutAppend(key string, value string, op string) { 94 | // You will have to modify this function. 95 | id := nrand() 96 | i := 0 97 | for { 98 | args := &PutAppendArgs{} 99 | args.Id = id 100 | args.Key = key 101 | args.Value = value 102 | args.Op = op 103 | var reply PutAppendReply 104 | ok := call(ck.servers[i], "KVPaxos.PutAppend", args, &reply) 105 | if !ok { 106 | i = (i + 1) % len(ck.servers) 107 | continue 108 | } 109 | return 110 | } 111 | } 112 | 113 | func (ck *Clerk) Put(key string, value string) { 114 | ck.PutAppend(key, value, "Put") 115 | } 116 | func (ck *Clerk) Append(key string, value string) { 117 | ck.PutAppend(key, value, "Append") 118 | } 119 | -------------------------------------------------------------------------------- /6.824-2015/src/kvpaxos/common.go: -------------------------------------------------------------------------------- 1 | package kvpaxos 2 | 3 | const ( 4 | OK = "OK" 5 | ErrNoKey = "ErrNoKey" 6 | ) 7 | 8 | type Err string 9 | 10 | // Put or Append 11 | type PutAppendArgs struct { 12 | // You'll have to add definitions here. 13 | Key string 14 | Value string 15 | Op string // "Put" or "Append" 16 | // You'll have to add definitions here. 17 | // Field names must start with capital letters, 18 | // otherwise RPC will break. 19 | Id int64 20 | } 21 | 22 | type PutAppendReply struct { 23 | Err Err 24 | } 25 | 26 | type GetArgs struct { 27 | Key string 28 | // You'll have to add definitions here. 29 | Id int64 30 | } 31 | 32 | type GetReply struct { 33 | Err Err 34 | Value string 35 | } 36 | -------------------------------------------------------------------------------- /6.824-2015/src/kvpaxos/server.go: -------------------------------------------------------------------------------- 1 | package kvpaxos 2 | 3 | import "net" 4 | import "fmt" 5 | import "net/rpc" 6 | import "log" 7 | import "paxos" 8 | import "sync" 9 | import "sync/atomic" 10 | import "os" 11 | import "syscall" 12 | import "encoding/gob" 13 | import "math/rand" 14 | import "time" 15 | 16 | const Debug = 0 17 | 18 | func DPrintf(format string, a ...interface{}) (n int, err error) { 19 | if Debug > 0 { 20 | log.Printf(format, a...) 21 | } 22 | return 23 | } 24 | 25 | type Op struct { 26 | // Your definitions here. 27 | // Field names must start with capital letters, 28 | // otherwise RPC will break. 29 | Op string 30 | Key string 31 | Value string 32 | Id int64 33 | } 34 | 35 | type KVPaxos struct { 36 | mu sync.Mutex 37 | l net.Listener 38 | me int 39 | dead int32 // for testing 40 | unreliable int32 // for testing 41 | px *paxos.Paxos 42 | 43 | // Your definitions here. 44 | database map[string]string 45 | currentSeq int 46 | clients map[int64]bool 47 | } 48 | 49 | func (kv *KVPaxos) wait(seq int) Op { 50 | to := 10 * time.Millisecond 51 | for { 52 | status, v := kv.px.Status(seq) 53 | if status == paxos.Decided { 54 | return v.(Op) 55 | } 56 | time.Sleep(to) 57 | if to < 10*time.Second { 58 | to *= 2 59 | } 60 | } 61 | } 62 | 63 | func (kv *KVPaxos) doOperation(op Op) { 64 | kv.clients[op.Id] = true 65 | if op.Op == "Put" { 66 | kv.database[op.Key] = op.Value 67 | } else if op.Op == "Append" { 68 | v, ok := kv.database[op.Key] 69 | if ok { 70 | kv.database[op.Key] = v + op.Value 71 | } else { 72 | kv.database[op.Key] = op.Value 73 | } 74 | } 75 | 76 | kv.px.Done(kv.currentSeq) 77 | kv.currentSeq += 1 78 | } 79 | 80 | func (kv *KVPaxos) runPaxos(op Op) { 81 | var accept Op 82 | for { 83 | status, v := kv.px.Status(kv.currentSeq) 84 | if status == paxos.Decided { 85 | accept = v.(Op) 86 | } else { 87 | kv.px.Start(kv.currentSeq, op) 88 | accept = kv.wait(kv.currentSeq) 89 | } 90 | kv.doOperation(accept) 91 | if accept.Id == op.Id { 92 | break 93 | } 94 | } 95 | } 96 | 97 | func (kv *KVPaxos) Get(args *GetArgs, reply *GetReply) error { 98 | // Your code here. 99 | kv.mu.Lock() 100 | defer kv.mu.Unlock() 101 | 102 | _, ok := kv.clients[args.Id] 103 | if ok { 104 | reply.Value = kv.database[args.Key] 105 | reply.Err = OK 106 | return nil 107 | } 108 | 109 | op := Op{Id: args.Id, Key: args.Key, Op: "GET"} 110 | kv.runPaxos(op) 111 | 112 | v, ok := kv.database[args.Key] 113 | if ok { 114 | reply.Value = v 115 | reply.Err = OK 116 | } else { 117 | reply.Err = ErrNoKey 118 | } 119 | 120 | return nil 121 | } 122 | 123 | func (kv *KVPaxos) PutAppend(args *PutAppendArgs, reply *PutAppendReply) error { 124 | // Your code here. 125 | kv.mu.Lock() 126 | defer kv.mu.Unlock() 127 | 128 | _, ok := kv.clients[args.Id] 129 | if ok { 130 | reply.Err = OK 131 | return nil 132 | } 133 | 134 | op := Op{Id: args.Id, Key: args.Key, Value: args.Value, Op: args.Op} 135 | kv.runPaxos(op) 136 | 137 | reply.Err = OK 138 | 139 | return nil 140 | } 141 | 142 | // tell the server to shut itself down. 143 | // please do not change these two functions. 144 | func (kv *KVPaxos) kill() { 145 | DPrintf("Kill(%d): die\n", kv.me) 146 | atomic.StoreInt32(&kv.dead, 1) 147 | kv.l.Close() 148 | kv.px.Kill() 149 | } 150 | 151 | // call this to find out if the server is dead. 152 | func (kv *KVPaxos) isdead() bool { 153 | return atomic.LoadInt32(&kv.dead) != 0 154 | } 155 | 156 | // please do not change these two functions. 157 | func (kv *KVPaxos) setunreliable(what bool) { 158 | if what { 159 | atomic.StoreInt32(&kv.unreliable, 1) 160 | } else { 161 | atomic.StoreInt32(&kv.unreliable, 0) 162 | } 163 | } 164 | 165 | func (kv *KVPaxos) isunreliable() bool { 166 | return atomic.LoadInt32(&kv.unreliable) != 0 167 | } 168 | 169 | // 170 | // servers[] contains the ports of the set of 171 | // servers that will cooperate via Paxos to 172 | // form the fault-tolerant key/value service. 173 | // me is the index of the current server in servers[]. 174 | // 175 | func StartServer(servers []string, me int) *KVPaxos { 176 | // call gob.Register on structures you want 177 | // Go's RPC library to marshall/unmarshall. 178 | gob.Register(Op{}) 179 | 180 | kv := new(KVPaxos) 181 | kv.me = me 182 | 183 | // Your initialization code here. 184 | kv.database = make(map[string]string) 185 | kv.currentSeq = 0 186 | kv.clients = make(map[int64]bool) 187 | 188 | rpcs := rpc.NewServer() 189 | rpcs.Register(kv) 190 | 191 | kv.px = paxos.Make(servers, me, rpcs) 192 | 193 | os.Remove(servers[me]) 194 | l, e := net.Listen("unix", servers[me]) 195 | if e != nil { 196 | log.Fatal("listen error: ", e) 197 | } 198 | kv.l = l 199 | 200 | // please do not change any of the following code, 201 | // or do anything to subvert it. 202 | 203 | go func() { 204 | for kv.isdead() == false { 205 | conn, err := kv.l.Accept() 206 | if err == nil && kv.isdead() == false { 207 | if kv.isunreliable() && (rand.Int63()%1000) < 100 { 208 | // discard the request. 209 | conn.Close() 210 | } else if kv.isunreliable() && (rand.Int63()%1000) < 200 { 211 | // process the request but force discard of reply. 212 | c1 := conn.(*net.UnixConn) 213 | f, _ := c1.File() 214 | err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR) 215 | if err != nil { 216 | fmt.Printf("shutdown: %v\n", err) 217 | } 218 | go rpcs.ServeConn(conn) 219 | } else { 220 | go rpcs.ServeConn(conn) 221 | } 222 | } else if err == nil { 223 | conn.Close() 224 | } 225 | if err != nil && kv.isdead() == false { 226 | fmt.Printf("KVPaxos(%v) accept: %v\n", me, err.Error()) 227 | kv.kill() 228 | } 229 | } 230 | }() 231 | 232 | return kv 233 | } 234 | -------------------------------------------------------------------------------- /6.824-2015/src/lockservice/client.go: -------------------------------------------------------------------------------- 1 | package lockservice 2 | 3 | import "net/rpc" 4 | import "fmt" 5 | 6 | // 7 | // the lockservice Clerk lives in the client 8 | // and maintains a little state. 9 | // 10 | type Clerk struct { 11 | servers [2]string // primary port, backup port 12 | // Your definitions here. 13 | } 14 | 15 | func MakeClerk(primary string, backup string) *Clerk { 16 | ck := new(Clerk) 17 | ck.servers[0] = primary 18 | ck.servers[1] = backup 19 | // Your initialization code here. 20 | return ck 21 | } 22 | 23 | // 24 | // call() sends an RPC to the rpcname handler on server srv 25 | // with arguments args, waits for the reply, and leaves the 26 | // reply in reply. the reply argument should be the address 27 | // of a reply structure. 28 | // 29 | // call() returns true if the server responded, and false 30 | // if call() was not able to contact the server. in particular, 31 | // reply's contents are valid if and only if call() returned true. 32 | // 33 | // you should assume that call() will return an 34 | // error after a while if the server is dead. 35 | // don't provide your own time-out mechanism. 36 | // 37 | // please use call() to send all RPCs, in client.go and server.go. 38 | // please don't change this function. 39 | // 40 | func call(srv string, rpcname string, 41 | args interface{}, reply interface{}) bool { 42 | c, errx := rpc.Dial("unix", srv) 43 | if errx != nil { 44 | return false 45 | } 46 | defer c.Close() 47 | 48 | err := c.Call(rpcname, args, reply) 49 | if err == nil { 50 | return true 51 | } 52 | 53 | fmt.Println(err) 54 | return false 55 | } 56 | 57 | // 58 | // ask the lock service for a lock. 59 | // returns true if the lock service 60 | // granted the lock, false otherwise. 61 | // 62 | // you will have to modify this function. 63 | // 64 | func (ck *Clerk) Lock(lockname string) bool { 65 | // prepare the arguments. 66 | args := &LockArgs{} 67 | args.Lockname = lockname 68 | var reply LockReply 69 | 70 | // send an RPC request, wait for the reply. 71 | ok := call(ck.servers[0], "LockServer.Lock", args, &reply) 72 | if ok == false { 73 | return false 74 | } 75 | 76 | return reply.OK 77 | } 78 | 79 | // 80 | // ask the lock service to unlock a lock. 81 | // returns true if the lock was previously held, 82 | // false otherwise. 83 | // 84 | 85 | func (ck *Clerk) Unlock(lockname string) bool { 86 | 87 | // Your code here. 88 | 89 | return false 90 | } 91 | -------------------------------------------------------------------------------- /6.824-2015/src/lockservice/common.go: -------------------------------------------------------------------------------- 1 | package lockservice 2 | 3 | // 4 | // RPC definitions for a simple lock service. 5 | // 6 | // You will need to modify this file. 7 | // 8 | 9 | // 10 | // Lock(lockname) returns OK=true if the lock is not held. 11 | // If it is held, it returns OK=false immediately. 12 | // 13 | type LockArgs struct { 14 | // Go's net/rpc requires that these field 15 | // names start with upper case letters! 16 | Lockname string // lock name 17 | } 18 | 19 | type LockReply struct { 20 | OK bool 21 | } 22 | 23 | // 24 | // Unlock(lockname) returns OK=true if the lock was held. 25 | // It returns OK=false if the lock was not held. 26 | // 27 | type UnlockArgs struct { 28 | Lockname string 29 | } 30 | 31 | type UnlockReply struct { 32 | OK bool 33 | } 34 | -------------------------------------------------------------------------------- /6.824-2015/src/lockservice/server.go: -------------------------------------------------------------------------------- 1 | package lockservice 2 | 3 | import "net" 4 | import "net/rpc" 5 | import "log" 6 | import "sync" 7 | import "fmt" 8 | import "os" 9 | import "io" 10 | import "time" 11 | 12 | type LockServer struct { 13 | mu sync.Mutex 14 | l net.Listener 15 | dead bool // for test_test.go 16 | dying bool // for test_test.go 17 | 18 | am_primary bool // am I the primary? 19 | backup string // backup's port 20 | 21 | // for each lock name, is it locked? 22 | locks map[string]bool 23 | } 24 | 25 | // 26 | // server Lock RPC handler. 27 | // 28 | // you will have to modify this function 29 | // 30 | func (ls *LockServer) Lock(args *LockArgs, reply *LockReply) error { 31 | ls.mu.Lock() 32 | defer ls.mu.Unlock() 33 | 34 | locked, _ := ls.locks[args.Lockname] 35 | 36 | if locked { 37 | reply.OK = false 38 | } else { 39 | reply.OK = true 40 | ls.locks[args.Lockname] = true 41 | } 42 | 43 | return nil 44 | } 45 | 46 | // 47 | // server Unlock RPC handler. 48 | // 49 | func (ls *LockServer) Unlock(args *UnlockArgs, reply *UnlockReply) error { 50 | 51 | // Your code here. 52 | 53 | return nil 54 | } 55 | 56 | // 57 | // tell the server to shut itself down. 58 | // for testing. 59 | // please don't change this. 60 | // 61 | func (ls *LockServer) kill() { 62 | ls.dead = true 63 | ls.l.Close() 64 | } 65 | 66 | // 67 | // hack to allow test_test.go to have primary process 68 | // an RPC but not send a reply. can't use the shutdown() 69 | // trick b/c that causes client to immediately get an 70 | // error and send to backup before primary does. 71 | // please don't change anything to do with DeafConn. 72 | // 73 | type DeafConn struct { 74 | c io.ReadWriteCloser 75 | } 76 | 77 | func (dc DeafConn) Write(p []byte) (n int, err error) { 78 | return len(p), nil 79 | } 80 | func (dc DeafConn) Close() error { 81 | return dc.c.Close() 82 | } 83 | func (dc DeafConn) Read(p []byte) (n int, err error) { 84 | return dc.c.Read(p) 85 | } 86 | 87 | func StartServer(primary string, backup string, am_primary bool) *LockServer { 88 | ls := new(LockServer) 89 | ls.backup = backup 90 | ls.am_primary = am_primary 91 | ls.locks = map[string]bool{} 92 | 93 | // Your initialization code here. 94 | 95 | me := "" 96 | if am_primary { 97 | me = primary 98 | } else { 99 | me = backup 100 | } 101 | 102 | // tell net/rpc about our RPC server and handlers. 103 | rpcs := rpc.NewServer() 104 | rpcs.Register(ls) 105 | 106 | // prepare to receive connections from clients. 107 | // change "unix" to "tcp" to use over a network. 108 | os.Remove(me) // only needed for "unix" 109 | l, e := net.Listen("unix", me) 110 | if e != nil { 111 | log.Fatal("listen error: ", e) 112 | } 113 | ls.l = l 114 | 115 | // please don't change any of the following code, 116 | // or do anything to subvert it. 117 | 118 | // create a thread to accept RPC connections from clients. 119 | go func() { 120 | for ls.dead == false { 121 | conn, err := ls.l.Accept() 122 | if err == nil && ls.dead == false { 123 | if ls.dying { 124 | // process the request but force discard of reply. 125 | 126 | // without this the connection is never closed, 127 | // b/c ServeConn() is waiting for more requests. 128 | // test_test.go depends on this two seconds. 129 | go func() { 130 | time.Sleep(2 * time.Second) 131 | conn.Close() 132 | }() 133 | ls.l.Close() 134 | 135 | // this object has the type ServeConn expects, 136 | // but discards writes (i.e. discards the RPC reply). 137 | deaf_conn := DeafConn{c: conn} 138 | 139 | rpcs.ServeConn(deaf_conn) 140 | 141 | ls.dead = true 142 | } else { 143 | go rpcs.ServeConn(conn) 144 | } 145 | } else if err == nil { 146 | conn.Close() 147 | } 148 | if err != nil && ls.dead == false { 149 | fmt.Printf("LockServer(%v) accept: %v\n", me, err.Error()) 150 | ls.kill() 151 | } 152 | } 153 | }() 154 | 155 | return ls 156 | } 157 | -------------------------------------------------------------------------------- /6.824-2015/src/main/diskvd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 4 | // start a diskvd server. it's a member of some replica 5 | // group, which has other members, and it needs to know 6 | // how to talk to the members of the shardmaster service. 7 | // used by ../diskv/test_test.go 8 | // 9 | // arguments: 10 | // -g groupid 11 | // -m masterport1 -m masterport2 ... 12 | // -s replicaport1 -s replicaport2 ... 13 | // -i my-index-in-server-port-list 14 | // -u unreliable 15 | // -d directory 16 | // -r restart 17 | 18 | import "time" 19 | import "diskv" 20 | import "os" 21 | import "fmt" 22 | import "strconv" 23 | import "runtime" 24 | 25 | func usage() { 26 | fmt.Printf("Usage: diskvd -g gid -m master... -s server... -i my-index -d dir\n") 27 | os.Exit(1) 28 | } 29 | 30 | func main() { 31 | var gid int64 = -1 // my replica group ID 32 | masters := []string{} // ports of shardmasters 33 | replicas := []string{} // ports of servers in my replica group 34 | me := -1 // my index in replicas[] 35 | unreliable := false 36 | dir := "" // store persistent data here 37 | restart := false 38 | 39 | for i := 1; i+1 < len(os.Args); i += 2 { 40 | a0 := os.Args[i] 41 | a1 := os.Args[i+1] 42 | if a0 == "-g" { 43 | gid, _ = strconv.ParseInt(a1, 10, 64) 44 | } else if a0 == "-m" { 45 | masters = append(masters, a1) 46 | } else if a0 == "-s" { 47 | replicas = append(replicas, a1) 48 | } else if a0 == "-i" { 49 | me, _ = strconv.Atoi(a1) 50 | } else if a0 == "-u" { 51 | unreliable, _ = strconv.ParseBool(a1) 52 | } else if a0 == "-d" { 53 | dir = a1 54 | } else if a0 == "-r" { 55 | restart, _ = strconv.ParseBool(a1) 56 | } else { 57 | usage() 58 | } 59 | } 60 | 61 | if gid < 0 || me < 0 || len(masters) < 1 || me >= len(replicas) || dir == "" { 62 | usage() 63 | } 64 | 65 | runtime.GOMAXPROCS(4) 66 | 67 | srv := diskv.StartServer(gid, masters, replicas, me, dir, restart) 68 | srv.Setunreliable(unreliable) 69 | 70 | // for safety, force quit after 10 minutes. 71 | time.Sleep(10 * 60 * time.Second) 72 | mep, _ := os.FindProcess(os.Getpid()) 73 | mep.Kill() 74 | } 75 | -------------------------------------------------------------------------------- /6.824-2015/src/main/lockc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 4 | // see comments in lockd.go 5 | // 6 | 7 | import "lockservice" 8 | import "os" 9 | import "fmt" 10 | 11 | func usage() { 12 | fmt.Printf("Usage: lockc -l|-u primaryport backupport lockname\n") 13 | os.Exit(1) 14 | } 15 | 16 | func main() { 17 | if len(os.Args) == 5 { 18 | ck := lockservice.MakeClerk(os.Args[2], os.Args[3]) 19 | var ok bool 20 | if os.Args[1] == "-l" { 21 | ok = ck.Lock(os.Args[4]) 22 | } else if os.Args[1] == "-u" { 23 | ok = ck.Unlock(os.Args[4]) 24 | } else { 25 | usage() 26 | } 27 | fmt.Printf("reply: %v\n", ok) 28 | } else { 29 | usage() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /6.824-2015/src/main/lockd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // export GOPATH=~/6.824 4 | // go build lockd.go 5 | // go build lockc.go 6 | // ./lockd -p a b & 7 | // ./lockd -b a b & 8 | // ./lockc -l a b lx 9 | // ./lockc -u a b lx 10 | // 11 | // on Athena, use /tmp/myname-a and /tmp/myname-b 12 | // instead of a and b. 13 | 14 | import "time" 15 | import "lockservice" 16 | import "os" 17 | import "fmt" 18 | 19 | func main() { 20 | if len(os.Args) == 4 && os.Args[1] == "-p" { 21 | lockservice.StartServer(os.Args[2], os.Args[3], true) 22 | } else if len(os.Args) == 4 && os.Args[1] == "-b" { 23 | lockservice.StartServer(os.Args[2], os.Args[3], false) 24 | } else { 25 | fmt.Printf("Usage: lockd -p|-b primaryport backupport\n") 26 | os.Exit(1) 27 | } 28 | for { 29 | time.Sleep(100 * time.Second) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /6.824-2015/src/main/mr-testout.txt: -------------------------------------------------------------------------------- 1 | unto: 8940 2 | he: 9666 3 | shall: 9760 4 | in: 12334 5 | that: 12577 6 | And: 12846 7 | to: 13384 8 | of: 34434 9 | and: 38850 10 | the: 62075 11 | -------------------------------------------------------------------------------- /6.824-2015/src/main/pbc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 4 | // pbservice client application 5 | // 6 | // export GOPATH=~/6.824 7 | // go build viewd.go 8 | // go build pbd.go 9 | // go build pbc.go 10 | // ./viewd /tmp/rtm-v & 11 | // ./pbd /tmp/rtm-v /tmp/rtm-1 & 12 | // ./pbd /tmp/rtm-v /tmp/rtm-2 & 13 | // ./pbc /tmp/rtm-v key1 value1 14 | // ./pbc /tmp/rtm-v key1 15 | // 16 | // change "rtm" to your user name. 17 | // start the pbd programs in separate windows and kill 18 | // and restart them to exercise fault tolerance. 19 | // 20 | 21 | import "pbservice" 22 | import "os" 23 | import "fmt" 24 | 25 | func usage() { 26 | fmt.Printf("Usage: pbc viewport key\n") 27 | fmt.Printf(" pbc viewport key value\n") 28 | os.Exit(1) 29 | } 30 | 31 | func main() { 32 | if len(os.Args) == 3 { 33 | // get 34 | ck := pbservice.MakeClerk(os.Args[1], "") 35 | v := ck.Get(os.Args[2]) 36 | fmt.Printf("%v\n", v) 37 | } else if len(os.Args) == 4 { 38 | // put 39 | ck := pbservice.MakeClerk(os.Args[1], "") 40 | ck.Put(os.Args[2], os.Args[3]) 41 | } else { 42 | usage() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /6.824-2015/src/main/pbd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 4 | // see directions in pbc.go 5 | // 6 | 7 | import "time" 8 | import "pbservice" 9 | import "os" 10 | import "fmt" 11 | 12 | func main() { 13 | if len(os.Args) != 3 { 14 | fmt.Printf("Usage: pbd viewport myport\n") 15 | os.Exit(1) 16 | } 17 | 18 | pbservice.StartServer(os.Args[1], os.Args[2]) 19 | 20 | for { 21 | time.Sleep(100 * time.Second) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /6.824-2015/src/main/test-wc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | go run wc.go master kjv12.txt sequential 3 | sort -n -k2 mrtmp.kjv12.txt | tail -10 | diff - mr-testout.txt > diff.out 4 | if [ -s diff.out ] 5 | then 6 | echo "Failed test. Output should be as in mr-testout.txt. Your output differs as follows (from diff.out):" 7 | cat diff.out 8 | else 9 | echo "Passed test" 10 | fi 11 | 12 | -------------------------------------------------------------------------------- /6.824-2015/src/main/viewd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 4 | // see directions in pbc.go 5 | // 6 | 7 | import "time" 8 | import "viewservice" 9 | import "os" 10 | import "fmt" 11 | 12 | func main() { 13 | if len(os.Args) != 2 { 14 | fmt.Printf("Usage: viewd port\n") 15 | os.Exit(1) 16 | } 17 | 18 | viewservice.StartServer(os.Args[1]) 19 | 20 | for { 21 | time.Sleep(100 * time.Second) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /6.824-2015/src/main/wc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "os" 4 | import "fmt" 5 | import "mapreduce" 6 | import "strings" 7 | import "strconv" 8 | import "unicode" 9 | 10 | import "container/list" 11 | 12 | func IsRune(ch rune) bool { 13 | if unicode.IsLetter(ch) { 14 | return false 15 | } 16 | 17 | return true 18 | } 19 | 20 | // our simplified version of MapReduce does not supply a 21 | // key to the Map function, as in the paper; only a value, 22 | // which is a part of the input file content. the return 23 | // value should be a list of key/value pairs, each represented 24 | // by a mapreduce.KeyValue. 25 | func Map(value string) *list.List { 26 | maplist := list.New() 27 | words := strings.FieldsFunc(value, IsRune) 28 | for _, word := range words { 29 | maplist.PushBack(mapreduce.KeyValue{word, "1"}) 30 | } 31 | return maplist 32 | } 33 | 34 | // called once for each key generated by Map, with a list 35 | // of that key's string value. should return a single 36 | // output value for that key. 37 | func Reduce(key string, values *list.List) string { 38 | count := 0 39 | for e := values.Front(); e != nil; e = e.Next() { 40 | i, _ := strconv.Atoi(e.Value.(string)) 41 | count += i 42 | } 43 | 44 | return strconv.Itoa(count) 45 | } 46 | 47 | // Can be run in 3 ways: 48 | // 1) Sequential (e.g., go run wc.go master x.txt sequential) 49 | // 2) Master (e.g., go run wc.go master x.txt localhost:7777) 50 | // 3) Worker (e.g., go run wc.go worker localhost:7777 localhost:7778 &) 51 | func main() { 52 | if len(os.Args) != 4 { 53 | fmt.Printf("%s: see usage comments in file\n", os.Args[0]) 54 | } else if os.Args[1] == "master" { 55 | if os.Args[3] == "sequential" { 56 | mapreduce.RunSingle(5, 3, os.Args[2], Map, Reduce) 57 | } else { 58 | mr := mapreduce.MakeMapReduce(5, 3, os.Args[2], os.Args[3]) 59 | // Wait until MR is done 60 | <-mr.DoneChannel 61 | } 62 | } else { 63 | mapreduce.RunWorker(os.Args[2], os.Args[3], Map, Reduce, 100) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /6.824-2015/src/mapreduce/common.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import "fmt" 4 | import "net/rpc" 5 | 6 | const ( 7 | Map = "Map" 8 | Reduce = "Reduce" 9 | ) 10 | 11 | type JobType string 12 | 13 | // RPC arguments and replies. Field names must start with capital letters, 14 | // otherwise RPC will break. 15 | 16 | type DoJobArgs struct { 17 | File string 18 | Operation JobType 19 | JobNumber int // this job's number 20 | NumOtherPhase int // total number of jobs in other phase (map or reduce) 21 | } 22 | 23 | type DoJobReply struct { 24 | OK bool 25 | } 26 | 27 | type ShutdownArgs struct { 28 | } 29 | 30 | type ShutdownReply struct { 31 | Njobs int 32 | OK bool 33 | } 34 | 35 | type RegisterArgs struct { 36 | Worker string 37 | } 38 | 39 | type RegisterReply struct { 40 | OK bool 41 | } 42 | 43 | // 44 | // call() sends an RPC to the rpcname handler on server srv 45 | // with arguments args, waits for the reply, and leaves the 46 | // reply in reply. the reply argument should be the address 47 | // of a reply structure. 48 | // 49 | // call() returns true if the server responded, and false 50 | // if call() was not able to contact the server. in particular, 51 | // reply's contents are valid if and only if call() returned true. 52 | // 53 | // you should assume that call() will time out and return an 54 | // error after a while if it doesn't get a reply from the server. 55 | // 56 | // please use call() to send all RPCs, in master.go, mapreduce.go, 57 | // and worker.go. please don't change this function. 58 | // 59 | func call(srv string, rpcname string, 60 | args interface{}, reply interface{}) bool { 61 | c, errx := rpc.Dial("unix", srv) 62 | if errx != nil { 63 | return false 64 | } 65 | defer c.Close() 66 | 67 | err := c.Call(rpcname, args, reply) 68 | if err == nil { 69 | return true 70 | } 71 | 72 | fmt.Println(err) 73 | return false 74 | } 75 | -------------------------------------------------------------------------------- /6.824-2015/src/mapreduce/master.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import "container/list" 4 | import "fmt" 5 | 6 | type WorkerInfo struct { 7 | address string 8 | // You can add definitions here. 9 | } 10 | 11 | // Clean up all workers by sending a Shutdown RPC to each one of them Collect 12 | // the number of jobs each work has performed. 13 | func (mr *MapReduce) KillWorkers() *list.List { 14 | l := list.New() 15 | for _, w := range mr.Workers { 16 | DPrintf("DoWork: shutdown %s\n", w.address) 17 | args := &ShutdownArgs{} 18 | var reply ShutdownReply 19 | ok := call(w.address, "Worker.Shutdown", args, &reply) 20 | if ok == false { 21 | fmt.Printf("DoWork: RPC %s shutdown error\n", w.address) 22 | } else { 23 | l.PushBack(reply.Njobs) 24 | } 25 | } 26 | return l 27 | } 28 | 29 | func (mr *MapReduce) RunMaster() *list.List { 30 | // Your code here 31 | donechannel := make(chan int) 32 | 33 | for i := 0; i < mr.nMap; i++ { 34 | go func(jobnumber int) { 35 | for { 36 | worker := <-mr.idleWorkerChannel 37 | args := &DoJobArgs{mr.file, Map, jobnumber, mr.nReduce} 38 | reply := &DoJobReply{} 39 | ok := call(worker, "Worker.DoJob", args, reply) 40 | if ok == true { 41 | mr.idleWorkerChannel <- worker 42 | donechannel <- jobnumber 43 | return 44 | } 45 | } 46 | }(i) 47 | } 48 | 49 | for i := 0; i < mr.nMap; i++ { 50 | <-donechannel 51 | } 52 | 53 | for i := 0; i < mr.nReduce; i++ { 54 | go func(jobnumber int) { 55 | for { 56 | worker := <-mr.idleWorkerChannel 57 | args := &DoJobArgs{mr.file, Reduce, jobnumber, mr.nMap} 58 | reply := &DoJobReply{} 59 | ok := call(worker, "Worker.DoJob", args, reply) 60 | if ok == true { 61 | mr.idleWorkerChannel <- worker 62 | donechannel <- jobnumber 63 | return 64 | } 65 | } 66 | }(i) 67 | } 68 | 69 | for i := 0; i < mr.nReduce; i++ { 70 | <-donechannel 71 | } 72 | 73 | return mr.KillWorkers() 74 | } 75 | -------------------------------------------------------------------------------- /6.824-2015/src/mapreduce/test_test.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import "testing" 4 | import "fmt" 5 | import "time" 6 | import "container/list" 7 | import "strings" 8 | import "os" 9 | import "bufio" 10 | import "log" 11 | import "sort" 12 | import "strconv" 13 | 14 | const ( 15 | nNumber = 100000 16 | nMap = 100 17 | nReduce = 50 18 | ) 19 | 20 | // Create input file with N numbers 21 | // Check if we have N numbers in output file 22 | 23 | // Split in words 24 | func MapFunc(value string) *list.List { 25 | DPrintf("Map %v\n", value) 26 | res := list.New() 27 | words := strings.Fields(value) 28 | for _, w := range words { 29 | kv := KeyValue{w, ""} 30 | res.PushBack(kv) 31 | } 32 | return res 33 | } 34 | 35 | // Just return key 36 | func ReduceFunc(key string, values *list.List) string { 37 | for e := values.Front(); e != nil; e = e.Next() { 38 | DPrintf("Reduce %s %v\n", key, e.Value) 39 | } 40 | return "" 41 | } 42 | 43 | // Checks input file agaist output file: each input number should show up 44 | // in the output file in string sorted order 45 | func check(t *testing.T, file string) { 46 | input, err := os.Open(file) 47 | if err != nil { 48 | log.Fatal("check: ", err) 49 | } 50 | defer input.Close() 51 | output, err := os.Open("mrtmp." + file) 52 | if err != nil { 53 | log.Fatal("check: ", err) 54 | } 55 | defer output.Close() 56 | 57 | var lines []string 58 | inputScanner := bufio.NewScanner(input) 59 | for inputScanner.Scan() { 60 | lines = append(lines, inputScanner.Text()) 61 | } 62 | 63 | sort.Strings(lines) 64 | 65 | outputScanner := bufio.NewScanner(output) 66 | i := 0 67 | for outputScanner.Scan() { 68 | var v1 int 69 | var v2 int 70 | text := outputScanner.Text() 71 | n, err := fmt.Sscanf(lines[i], "%d", &v1) 72 | if n == 1 && err == nil { 73 | n, err = fmt.Sscanf(text, "%d", &v2) 74 | } 75 | if err != nil || v1 != v2 { 76 | t.Fatalf("line %d: %d != %d err %v\n", i, v1, v2, err) 77 | } 78 | i += 1 79 | } 80 | if i != nNumber { 81 | t.Fatalf("Expected %d lines in output\n", nNumber) 82 | } 83 | } 84 | 85 | // Workers report back how many RPCs they have processed in the Shutdown reply. 86 | // Check that they processed at least 1 RPC. 87 | func checkWorker(t *testing.T, l *list.List) { 88 | for e := l.Front(); e != nil; e = e.Next() { 89 | if e.Value == 0 { 90 | t.Fatalf("Some worker didn't do any work\n") 91 | } 92 | } 93 | } 94 | 95 | // Make input file 96 | func makeInput() string { 97 | name := "824-mrinput.txt" 98 | file, err := os.Create(name) 99 | if err != nil { 100 | log.Fatal("mkInput: ", err) 101 | } 102 | w := bufio.NewWriter(file) 103 | for i := 0; i < nNumber; i++ { 104 | fmt.Fprintf(w, "%d\n", i) 105 | } 106 | w.Flush() 107 | file.Close() 108 | return name 109 | } 110 | 111 | // Cook up a unique-ish UNIX-domain socket name 112 | // in /var/tmp. can't use current directory since 113 | // AFS doesn't support UNIX-domain sockets. 114 | func port(suffix string) string { 115 | s := "/var/tmp/824-" 116 | s += strconv.Itoa(os.Getuid()) + "/" 117 | os.Mkdir(s, 0777) 118 | s += "mr" 119 | s += strconv.Itoa(os.Getpid()) + "-" 120 | s += suffix 121 | return s 122 | } 123 | 124 | func setup() *MapReduce { 125 | file := makeInput() 126 | master := port("master") 127 | mr := MakeMapReduce(nMap, nReduce, file, master) 128 | return mr 129 | } 130 | 131 | func cleanup(mr *MapReduce) { 132 | mr.CleanupFiles() 133 | RemoveFile(mr.file) 134 | } 135 | 136 | func TestBasic(t *testing.T) { 137 | fmt.Printf("Test: Basic mapreduce ...\n") 138 | mr := setup() 139 | for i := 0; i < 2; i++ { 140 | go RunWorker(mr.MasterAddress, port("worker"+strconv.Itoa(i)), 141 | MapFunc, ReduceFunc, -1) 142 | } 143 | // Wait until MR is done 144 | <-mr.DoneChannel 145 | check(t, mr.file) 146 | checkWorker(t, mr.stats) 147 | cleanup(mr) 148 | fmt.Printf(" ... Basic Passed\n") 149 | } 150 | 151 | func TestOneFailure(t *testing.T) { 152 | fmt.Printf("Test: One Failure mapreduce ...\n") 153 | mr := setup() 154 | // Start 2 workers that fail after 10 jobs 155 | go RunWorker(mr.MasterAddress, port("worker"+strconv.Itoa(0)), 156 | MapFunc, ReduceFunc, 10) 157 | go RunWorker(mr.MasterAddress, port("worker"+strconv.Itoa(1)), 158 | MapFunc, ReduceFunc, -1) 159 | // Wait until MR is done 160 | <-mr.DoneChannel 161 | check(t, mr.file) 162 | checkWorker(t, mr.stats) 163 | cleanup(mr) 164 | fmt.Printf(" ... One Failure Passed\n") 165 | } 166 | 167 | func TestManyFailures(t *testing.T) { 168 | fmt.Printf("Test: One ManyFailures mapreduce ...\n") 169 | mr := setup() 170 | i := 0 171 | done := false 172 | for !done { 173 | select { 174 | case done = <-mr.DoneChannel: 175 | check(t, mr.file) 176 | cleanup(mr) 177 | break 178 | default: 179 | // Start 2 workers each sec. The workers fail after 10 jobs 180 | w := port("worker" + strconv.Itoa(i)) 181 | go RunWorker(mr.MasterAddress, w, MapFunc, ReduceFunc, 10) 182 | i++ 183 | w = port("worker" + strconv.Itoa(i)) 184 | go RunWorker(mr.MasterAddress, w, MapFunc, ReduceFunc, 10) 185 | i++ 186 | time.Sleep(1 * time.Second) 187 | } 188 | } 189 | 190 | fmt.Printf(" ... Many Failures Passed\n") 191 | } 192 | -------------------------------------------------------------------------------- /6.824-2015/src/mapreduce/worker.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import "fmt" 4 | import "os" 5 | import "log" 6 | import "net/rpc" 7 | import "net" 8 | import "container/list" 9 | 10 | // Worker is a server waiting for DoJob or Shutdown RPCs 11 | 12 | type Worker struct { 13 | name string 14 | Reduce func(string, *list.List) string 15 | Map func(string) *list.List 16 | nRPC int 17 | nJobs int 18 | l net.Listener 19 | } 20 | 21 | // The master sent us a job 22 | func (wk *Worker) DoJob(arg *DoJobArgs, res *DoJobReply) error { 23 | fmt.Printf("Dojob %s job %d file %s operation %v N %d\n", 24 | wk.name, arg.JobNumber, arg.File, arg.Operation, 25 | arg.NumOtherPhase) 26 | switch arg.Operation { 27 | case Map: 28 | DoMap(arg.JobNumber, arg.File, arg.NumOtherPhase, wk.Map) 29 | case Reduce: 30 | DoReduce(arg.JobNumber, arg.File, arg.NumOtherPhase, wk.Reduce) 31 | } 32 | res.OK = true 33 | return nil 34 | } 35 | 36 | // The master is telling us to shutdown. Report the number of Jobs we 37 | // have processed. 38 | func (wk *Worker) Shutdown(args *ShutdownArgs, res *ShutdownReply) error { 39 | DPrintf("Shutdown %s\n", wk.name) 40 | res.Njobs = wk.nJobs 41 | res.OK = true 42 | wk.nRPC = 1 // OK, because the same thread reads nRPC 43 | wk.nJobs-- // Don't count the shutdown RPC 44 | return nil 45 | } 46 | 47 | // Tell the master we exist and ready to work 48 | func Register(master string, me string) { 49 | args := &RegisterArgs{} 50 | args.Worker = me 51 | var reply RegisterReply 52 | ok := call(master, "MapReduce.Register", args, &reply) 53 | if ok == false { 54 | fmt.Printf("Register: RPC %s register error\n", master) 55 | } 56 | } 57 | 58 | // Set up a connection with the master, register with the master, 59 | // and wait for jobs from the master 60 | func RunWorker(MasterAddress string, me string, 61 | MapFunc func(string) *list.List, 62 | ReduceFunc func(string, *list.List) string, nRPC int) { 63 | DPrintf("RunWorker %s\n", me) 64 | wk := new(Worker) 65 | wk.name = me 66 | wk.Map = MapFunc 67 | wk.Reduce = ReduceFunc 68 | wk.nRPC = nRPC 69 | rpcs := rpc.NewServer() 70 | rpcs.Register(wk) 71 | os.Remove(me) // only needed for "unix" 72 | l, e := net.Listen("unix", me) 73 | if e != nil { 74 | log.Fatal("RunWorker: worker ", me, " error: ", e) 75 | } 76 | wk.l = l 77 | Register(MasterAddress, me) 78 | 79 | // DON'T MODIFY CODE BELOW 80 | for wk.nRPC != 0 { 81 | conn, err := wk.l.Accept() 82 | if err == nil { 83 | wk.nRPC -= 1 84 | go rpcs.ServeConn(conn) 85 | wk.nJobs += 1 86 | } else { 87 | break 88 | } 89 | } 90 | wk.l.Close() 91 | DPrintf("RunWorker %s exit\n", me) 92 | } 93 | -------------------------------------------------------------------------------- /6.824-2015/src/pbservice/client.go: -------------------------------------------------------------------------------- 1 | package pbservice 2 | 3 | import "viewservice" 4 | import "net/rpc" 5 | import "fmt" 6 | import "time" 7 | 8 | import "crypto/rand" 9 | import "math/big" 10 | 11 | type Clerk struct { 12 | vs *viewservice.Clerk 13 | // Your declarations here 14 | primary string 15 | } 16 | 17 | // this may come in handy. 18 | func nrand() int64 { 19 | max := big.NewInt(int64(1) << 62) 20 | bigx, _ := rand.Int(rand.Reader, max) 21 | x := bigx.Int64() 22 | return x 23 | } 24 | 25 | func MakeClerk(vshost string, me string) *Clerk { 26 | ck := new(Clerk) 27 | ck.vs = viewservice.MakeClerk(me, vshost) 28 | // Your ck.* initializations here 29 | ck.primary = "" 30 | 31 | return ck 32 | } 33 | 34 | // 35 | // call() sends an RPC to the rpcname handler on server srv 36 | // with arguments args, waits for the reply, and leaves the 37 | // reply in reply. the reply argument should be a pointer 38 | // to a reply structure. 39 | // 40 | // the return value is true if the server responded, and false 41 | // if call() was not able to contact the server. in particular, 42 | // the reply's contents are only valid if call() returned true. 43 | // 44 | // you should assume that call() will return an 45 | // error after a while if the server is dead. 46 | // don't provide your own time-out mechanism. 47 | // 48 | // please use call() to send all RPCs, in client.go and server.go. 49 | // please don't change this function. 50 | // 51 | func call(srv string, rpcname string, 52 | args interface{}, reply interface{}) bool { 53 | c, errx := rpc.Dial("unix", srv) 54 | if errx != nil { 55 | return false 56 | } 57 | defer c.Close() 58 | 59 | err := c.Call(rpcname, args, reply) 60 | if err == nil { 61 | return true 62 | } 63 | 64 | fmt.Println(err) 65 | return false 66 | } 67 | 68 | func (ck *Clerk) updateCurrentPrimary() { 69 | for ck.primary == "" { 70 | ck.primary = ck.vs.Primary() 71 | time.Sleep(viewservice.PingInterval) 72 | } 73 | } 74 | 75 | func (ck *Clerk) loopRPCGet(args *GetArgs, reply *GetReply) { 76 | for i := 0; i < viewservice.DeadPings; i++ { 77 | ok := call(ck.primary, "PBServer.Get", args, reply) 78 | if ok { 79 | break 80 | } 81 | time.Sleep(viewservice.PingInterval) 82 | } 83 | } 84 | 85 | func (ck *Clerk) loopRPCPutAppend(args *PutAppendArgs, reply *PutAppendReply) { 86 | for i := 0; i < viewservice.DeadPings; i++ { 87 | ok := call(ck.primary, "PBServer.PutAppend", args, reply) 88 | if ok { 89 | break 90 | } 91 | time.Sleep(viewservice.PingInterval) 92 | } 93 | } 94 | 95 | // 96 | // fetch a key's value from the current primary; 97 | // if they key has never been set, return "". 98 | // Get() must keep trying until it either the 99 | // primary replies with the value or the primary 100 | // says the key doesn't exist (has never been Put(). 101 | // 102 | func (ck *Clerk) Get(key string) string { 103 | 104 | // Your code here. 105 | id := nrand() 106 | for { 107 | ck.updateCurrentPrimary() 108 | args := &GetArgs{} 109 | args.Key = key 110 | args.Id = id 111 | var reply GetReply 112 | 113 | ck.loopRPCGet(args, &reply) 114 | 115 | if reply.Err == ErrNoKey { 116 | return "" 117 | } 118 | 119 | if reply.Err == ErrWrongServer || reply.Err == "" { 120 | ck.primary = "" 121 | continue 122 | } 123 | 124 | return reply.Value 125 | } 126 | } 127 | 128 | // 129 | // send a Put or Append RPC 130 | // 131 | func (ck *Clerk) PutAppend(key string, value string, op string) { 132 | 133 | // Your code here. 134 | id := nrand() 135 | for { 136 | ck.updateCurrentPrimary() 137 | args := &PutAppendArgs{} 138 | args.Key = key 139 | args.Value = value 140 | args.Op = op 141 | args.Id = id 142 | var reply PutAppendReply 143 | 144 | ck.loopRPCPutAppend(args, &reply) 145 | 146 | if reply.Err == ErrWrongServer || reply.Err == "" { 147 | ck.primary = "" 148 | continue 149 | } 150 | 151 | return 152 | } 153 | } 154 | 155 | // 156 | // tell the primary to update key's value. 157 | // must keep trying until it succeeds. 158 | // 159 | func (ck *Clerk) Put(key string, value string) { 160 | ck.PutAppend(key, value, "Put") 161 | } 162 | 163 | // 164 | // tell the primary to append to key's value. 165 | // must keep trying until it succeeds. 166 | // 167 | func (ck *Clerk) Append(key string, value string) { 168 | ck.PutAppend(key, value, "Append") 169 | } 170 | -------------------------------------------------------------------------------- /6.824-2015/src/pbservice/common.go: -------------------------------------------------------------------------------- 1 | package pbservice 2 | 3 | const ( 4 | OK = "OK" 5 | ErrNoKey = "ErrNoKey" 6 | ErrWrongServer = "ErrWrongServer" 7 | ) 8 | 9 | type Err string 10 | 11 | // Put or Append 12 | type PutAppendArgs struct { 13 | Key string 14 | Value string 15 | // You'll have to add definitions here. 16 | Op string 17 | Id int64 18 | 19 | // Field names must start with capital letters, 20 | // otherwise RPC will break. 21 | } 22 | 23 | type PutAppendReply struct { 24 | Err Err 25 | } 26 | 27 | type GetArgs struct { 28 | Key string 29 | // You'll have to add definitions here. 30 | Id int64 31 | } 32 | 33 | type GetReply struct { 34 | Err Err 35 | Value string 36 | } 37 | 38 | // Your RPC definitions here. 39 | type CopyArgs struct { 40 | Database map[string]string 41 | Clients map[int64]string 42 | } 43 | 44 | type CopyReply struct { 45 | Err Err 46 | } 47 | -------------------------------------------------------------------------------- /6.824-2015/src/pbservice/server.go: -------------------------------------------------------------------------------- 1 | package pbservice 2 | 3 | import "net" 4 | import "fmt" 5 | import "net/rpc" 6 | import "log" 7 | import "time" 8 | import "viewservice" 9 | import "sync" 10 | import "sync/atomic" 11 | import "os" 12 | import "syscall" 13 | import "math/rand" 14 | 15 | type PBServer struct { 16 | mu sync.Mutex 17 | l net.Listener 18 | dead int32 // for testing 19 | unreliable int32 // for testing 20 | me string 21 | vs *viewservice.Clerk 22 | // Your declarations here. 23 | view viewservice.View 24 | database map[string]string 25 | clients map[int64]string 26 | } 27 | 28 | func (pb *PBServer) get(args *GetArgs, reply *GetReply) { 29 | value, ok := pb.database[args.Key] 30 | if ok { 31 | reply.Value = value 32 | reply.Err = OK 33 | } else { 34 | reply.Err = ErrNoKey 35 | } 36 | 37 | pb.clients[args.Id] = reply.Value 38 | } 39 | 40 | func (pb *PBServer) Get(args *GetArgs, reply *GetReply) error { 41 | 42 | // Your code here. 43 | pb.mu.Lock() 44 | defer pb.mu.Unlock() 45 | 46 | v, ok := pb.clients[args.Id] 47 | if ok { 48 | reply.Value = v 49 | reply.Err = OK 50 | return nil 51 | } 52 | 53 | if pb.view.Primary != pb.me { 54 | reply.Err = ErrWrongServer 55 | return nil 56 | } 57 | 58 | if pb.view.Backup != "" { 59 | ok := call(pb.view.Backup, "PBServer.BackupGet", args, reply) 60 | if ok == false { 61 | return nil 62 | } 63 | 64 | if reply.Err != OK { 65 | return nil 66 | } 67 | } 68 | 69 | pb.get(args, reply) 70 | 71 | return nil 72 | } 73 | 74 | func (pb *PBServer) BackupGet(args *GetArgs, reply *GetReply) error { 75 | pb.mu.Lock() 76 | defer pb.mu.Unlock() 77 | 78 | if pb.view.Backup != pb.me { 79 | reply.Err = ErrWrongServer 80 | return nil 81 | } 82 | 83 | pb.get(args, reply) 84 | 85 | return nil 86 | } 87 | 88 | func (pb *PBServer) put(args *PutAppendArgs, reply *PutAppendReply) { 89 | if args.Op == "Put" { 90 | pb.database[args.Key] = args.Value 91 | } else { 92 | value := pb.database[args.Key] 93 | value += args.Value 94 | pb.database[args.Key] = value 95 | } 96 | reply.Err = OK 97 | 98 | pb.clients[args.Id] = "accepted" 99 | } 100 | 101 | func (pb *PBServer) PutAppend(args *PutAppendArgs, reply *PutAppendReply) error { 102 | 103 | // Your code here. 104 | pb.mu.Lock() 105 | defer pb.mu.Unlock() 106 | 107 | _, ok := pb.clients[args.Id] 108 | if ok { 109 | reply.Err = OK 110 | return nil 111 | } 112 | 113 | if pb.view.Primary != pb.me { 114 | reply.Err = ErrWrongServer 115 | return nil 116 | } 117 | 118 | if pb.view.Backup != "" { 119 | ok := call(pb.view.Backup, "PBServer.BackupPutAppend", args, reply) 120 | if ok == false { 121 | return nil 122 | } 123 | 124 | if reply.Err != OK { 125 | return nil 126 | } 127 | } 128 | 129 | pb.put(args, reply) 130 | 131 | return nil 132 | } 133 | 134 | func (pb *PBServer) BackupPutAppend(args *PutAppendArgs, reply *PutAppendReply) error { 135 | pb.mu.Lock() 136 | defer pb.mu.Unlock() 137 | 138 | if pb.view.Backup != pb.me { 139 | reply.Err = ErrWrongServer 140 | return nil 141 | } 142 | 143 | pb.put(args, reply) 144 | 145 | return nil 146 | } 147 | 148 | func (pb *PBServer) Copy(args *CopyArgs, reply *CopyReply) error { 149 | pb.mu.Lock() 150 | defer pb.mu.Unlock() 151 | 152 | pb.database = args.Database 153 | pb.clients = args.Clients 154 | 155 | return nil 156 | } 157 | 158 | // 159 | // ping the viewserver periodically. 160 | // if view changed: 161 | // transition to new view. 162 | // manage transfer of state from primary to new backup. 163 | // 164 | func (pb *PBServer) tick() { 165 | 166 | // Your code here. 167 | pb.mu.Lock() 168 | defer pb.mu.Unlock() 169 | 170 | view, err := pb.vs.Ping(pb.view.Viewnum) 171 | if err != nil { 172 | return 173 | } 174 | 175 | if view.Primary == pb.me && view.Backup != "" && view.Backup != pb.view.Backup { 176 | args := &CopyArgs{} 177 | args.Database = pb.database 178 | args.Clients = pb.clients 179 | var reply CopyReply 180 | ok := call(view.Backup, "PBServer.Copy", args, &reply) 181 | if ok == false { 182 | return 183 | } 184 | } 185 | 186 | pb.view = view 187 | } 188 | 189 | // tell the server to shut itself down. 190 | // please do not change these two functions. 191 | func (pb *PBServer) kill() { 192 | atomic.StoreInt32(&pb.dead, 1) 193 | pb.l.Close() 194 | } 195 | 196 | // call this to find out if the server is dead. 197 | func (pb *PBServer) isdead() bool { 198 | return atomic.LoadInt32(&pb.dead) != 0 199 | } 200 | 201 | // please do not change these two functions. 202 | func (pb *PBServer) setunreliable(what bool) { 203 | if what { 204 | atomic.StoreInt32(&pb.unreliable, 1) 205 | } else { 206 | atomic.StoreInt32(&pb.unreliable, 0) 207 | } 208 | } 209 | 210 | func (pb *PBServer) isunreliable() bool { 211 | return atomic.LoadInt32(&pb.unreliable) != 0 212 | } 213 | 214 | func StartServer(vshost string, me string) *PBServer { 215 | pb := new(PBServer) 216 | pb.me = me 217 | pb.vs = viewservice.MakeClerk(me, vshost) 218 | // Your pb.* initializations here. 219 | pb.view = viewservice.View{} 220 | pb.database = make(map[string]string) 221 | pb.clients = make(map[int64]string) 222 | 223 | rpcs := rpc.NewServer() 224 | rpcs.Register(pb) 225 | 226 | os.Remove(pb.me) 227 | l, e := net.Listen("unix", pb.me) 228 | if e != nil { 229 | log.Fatal("listen error: ", e) 230 | } 231 | pb.l = l 232 | 233 | // please do not change any of the following code, 234 | // or do anything to subvert it. 235 | 236 | go func() { 237 | for pb.isdead() == false { 238 | conn, err := pb.l.Accept() 239 | if err == nil && pb.isdead() == false { 240 | if pb.isunreliable() && (rand.Int63()%1000) < 100 { 241 | // discard the request. 242 | conn.Close() 243 | } else if pb.isunreliable() && (rand.Int63()%1000) < 200 { 244 | // process the request but force discard of reply. 245 | c1 := conn.(*net.UnixConn) 246 | f, _ := c1.File() 247 | err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR) 248 | if err != nil { 249 | fmt.Printf("shutdown: %v\n", err) 250 | } 251 | go rpcs.ServeConn(conn) 252 | } else { 253 | go rpcs.ServeConn(conn) 254 | } 255 | } else if err == nil { 256 | conn.Close() 257 | } 258 | if err != nil && pb.isdead() == false { 259 | fmt.Printf("PBServer(%v) accept: %v\n", me, err.Error()) 260 | pb.kill() 261 | } 262 | } 263 | }() 264 | 265 | go func() { 266 | for pb.isdead() == false { 267 | pb.tick() 268 | time.Sleep(viewservice.PingInterval) 269 | } 270 | }() 271 | 272 | return pb 273 | } 274 | -------------------------------------------------------------------------------- /6.824-2015/src/shardkv/client.go: -------------------------------------------------------------------------------- 1 | package shardkv 2 | 3 | import "shardmaster" 4 | import "net/rpc" 5 | import "time" 6 | import "sync" 7 | import "fmt" 8 | import "crypto/rand" 9 | import "math/big" 10 | import "strconv" 11 | 12 | type Clerk struct { 13 | mu sync.Mutex // one RPC at a time 14 | sm *shardmaster.Clerk 15 | config shardmaster.Config 16 | // You'll have to modify Clerk. 17 | me string 18 | seq int64 19 | } 20 | 21 | func nrand() int64 { 22 | max := big.NewInt(int64(1) << 62) 23 | bigx, _ := rand.Int(rand.Reader, max) 24 | x := bigx.Int64() 25 | return x 26 | } 27 | 28 | func MakeClerk(shardmasters []string) *Clerk { 29 | ck := new(Clerk) 30 | ck.sm = shardmaster.MakeClerk(shardmasters) 31 | // You'll have to modify MakeClerk. 32 | ck.me = strconv.FormatInt(nrand(), 10) 33 | ck.seq = 0 34 | return ck 35 | } 36 | 37 | // 38 | // call() sends an RPC to the rpcname handler on server srv 39 | // with arguments args, waits for the reply, and leaves the 40 | // reply in reply. the reply argument should be a pointer 41 | // to a reply structure. 42 | // 43 | // the return value is true if the server responded, and false 44 | // if call() was not able to contact the server. in particular, 45 | // the reply's contents are only valid if call() returned true. 46 | // 47 | // you should assume that call() will return an 48 | // error after a while if the server is dead. 49 | // don't provide your own time-out mechanism. 50 | // 51 | // please use call() to send all RPCs, in client.go and server.go. 52 | // please don't change this function. 53 | // 54 | func call(srv string, rpcname string, 55 | args interface{}, reply interface{}) bool { 56 | c, errx := rpc.Dial("unix", srv) 57 | if errx != nil { 58 | return false 59 | } 60 | defer c.Close() 61 | 62 | err := c.Call(rpcname, args, reply) 63 | if err == nil { 64 | return true 65 | } 66 | 67 | fmt.Println(err) 68 | return false 69 | } 70 | 71 | // 72 | // which shard is a key in? 73 | // please use this function, 74 | // and please do not change it. 75 | // 76 | func key2shard(key string) int { 77 | shard := 0 78 | if len(key) > 0 { 79 | shard = int(key[0]) 80 | } 81 | shard %= shardmaster.NShards 82 | return shard 83 | } 84 | 85 | // 86 | // fetch the current value for a key. 87 | // returns "" if the key does not exist. 88 | // keeps trying forever in the face of all other errors. 89 | // 90 | func (ck *Clerk) Get(key string) string { 91 | ck.mu.Lock() 92 | defer ck.mu.Unlock() 93 | 94 | // You'll have to modify Get(). 95 | ck.seq++ 96 | 97 | for { 98 | shard := key2shard(key) 99 | 100 | gid := ck.config.Shards[shard] 101 | 102 | servers, ok := ck.config.Groups[gid] 103 | 104 | if ok { 105 | // try each server in the shard's replication group. 106 | for _, srv := range servers { 107 | args := &GetArgs{} 108 | args.Key = key 109 | args.Me = ck.me 110 | args.Seq = ck.seq 111 | var reply GetReply 112 | ok := call(srv, "ShardKV.Get", args, &reply) 113 | if ok && (reply.Err == OK || reply.Err == ErrNoKey) { 114 | return reply.Value 115 | } 116 | if ok && (reply.Err == ErrWrongGroup) { 117 | break 118 | } 119 | } 120 | } 121 | 122 | time.Sleep(100 * time.Millisecond) 123 | 124 | // ask master for a new configuration. 125 | ck.config = ck.sm.Query(-1) 126 | } 127 | } 128 | 129 | // send a Put or Append request. 130 | func (ck *Clerk) PutAppend(key string, value string, op string) { 131 | ck.mu.Lock() 132 | defer ck.mu.Unlock() 133 | 134 | // You'll have to modify PutAppend(). 135 | ck.seq++ 136 | 137 | for { 138 | shard := key2shard(key) 139 | 140 | gid := ck.config.Shards[shard] 141 | 142 | servers, ok := ck.config.Groups[gid] 143 | 144 | if ok { 145 | // try each server in the shard's replication group. 146 | for _, srv := range servers { 147 | args := &PutAppendArgs{} 148 | args.Key = key 149 | args.Value = value 150 | args.Op = op 151 | args.Seq = ck.seq 152 | args.Me = ck.me 153 | var reply PutAppendReply 154 | ok := call(srv, "ShardKV.PutAppend", args, &reply) 155 | if ok && reply.Err == OK { 156 | return 157 | } 158 | if ok && (reply.Err == ErrWrongGroup) { 159 | break 160 | } 161 | } 162 | } 163 | 164 | time.Sleep(100 * time.Millisecond) 165 | 166 | // ask master for a new configuration. 167 | ck.config = ck.sm.Query(-1) 168 | } 169 | } 170 | 171 | func (ck *Clerk) Put(key string, value string) { 172 | ck.PutAppend(key, value, "Put") 173 | } 174 | func (ck *Clerk) Append(key string, value string) { 175 | ck.PutAppend(key, value, "Append") 176 | } 177 | -------------------------------------------------------------------------------- /6.824-2015/src/shardkv/common.go: -------------------------------------------------------------------------------- 1 | package shardkv 2 | 3 | import "shardmaster" 4 | 5 | // 6 | // Sharded key/value server. 7 | // Lots of replica groups, each running op-at-a-time paxos. 8 | // Shardmaster decides which group serves each shard. 9 | // Shardmaster may change shard assignment from time to time. 10 | // 11 | // You will have to modify these definitions. 12 | // 13 | 14 | const ( 15 | OK = "OK" 16 | ErrNoKey = "ErrNoKey" 17 | ErrWrongGroup = "ErrWrongGroup" 18 | ErrNotReady = "ErrNotReady" 19 | ) 20 | 21 | type Err string 22 | 23 | type PutAppendArgs struct { 24 | Key string 25 | Value string 26 | Op string // "Put" or "Append" 27 | // You'll have to add definitions here. 28 | // Field names must start with capital letters, 29 | // otherwise RPC will break. 30 | Me string 31 | Seq int64 32 | } 33 | 34 | type PutAppendReply struct { 35 | Err Err 36 | } 37 | 38 | type GetArgs struct { 39 | Key string 40 | // You'll have to add definitions here. 41 | Me string 42 | Seq int64 43 | } 44 | 45 | type GetReply struct { 46 | Err Err 47 | Value string 48 | } 49 | 50 | type GetShardArgs struct { 51 | Shard int 52 | Config shardmaster.Config 53 | } 54 | 55 | type GetShardReply struct { 56 | Err Err 57 | Content map[string]string 58 | Seen map[string]int64 59 | Replies map[string]string 60 | } 61 | -------------------------------------------------------------------------------- /6.824-2015/src/shardmaster/client.go: -------------------------------------------------------------------------------- 1 | package shardmaster 2 | 3 | // 4 | // Shardmaster clerk. 5 | // Please don't change this file. 6 | // 7 | 8 | import "net/rpc" 9 | import "time" 10 | import "fmt" 11 | 12 | type Clerk struct { 13 | servers []string // shardmaster replicas 14 | } 15 | 16 | func MakeClerk(servers []string) *Clerk { 17 | ck := new(Clerk) 18 | ck.servers = servers 19 | return ck 20 | } 21 | 22 | // 23 | // call() sends an RPC to the rpcname handler on server srv 24 | // with arguments args, waits for the reply, and leaves the 25 | // reply in reply. the reply argument should be a pointer 26 | // to a reply structure. 27 | // 28 | // the return value is true if the server responded, and false 29 | // if call() was not able to contact the server. in particular, 30 | // the reply's contents are only valid if call() returned true. 31 | // 32 | // you should assume that call() will return an 33 | // error after a while if the server is dead. 34 | // don't provide your own time-out mechanism. 35 | // 36 | // please use call() to send all RPCs, in client.go and server.go. 37 | // please don't change this function. 38 | // 39 | func call(srv string, rpcname string, 40 | args interface{}, reply interface{}) bool { 41 | c, errx := rpc.Dial("unix", srv) 42 | if errx != nil { 43 | return false 44 | } 45 | defer c.Close() 46 | 47 | err := c.Call(rpcname, args, reply) 48 | if err == nil { 49 | return true 50 | } 51 | 52 | fmt.Println(err) 53 | return false 54 | } 55 | 56 | func (ck *Clerk) Query(num int) Config { 57 | for { 58 | // try each known server. 59 | for _, srv := range ck.servers { 60 | args := &QueryArgs{} 61 | args.Num = num 62 | var reply QueryReply 63 | ok := call(srv, "ShardMaster.Query", args, &reply) 64 | if ok { 65 | return reply.Config 66 | } 67 | } 68 | time.Sleep(100 * time.Millisecond) 69 | } 70 | } 71 | 72 | func (ck *Clerk) Join(gid int64, servers []string) { 73 | for { 74 | // try each known server. 75 | for _, srv := range ck.servers { 76 | args := &JoinArgs{} 77 | args.GID = gid 78 | args.Servers = servers 79 | var reply JoinReply 80 | ok := call(srv, "ShardMaster.Join", args, &reply) 81 | if ok { 82 | return 83 | } 84 | } 85 | time.Sleep(100 * time.Millisecond) 86 | } 87 | } 88 | 89 | func (ck *Clerk) Leave(gid int64) { 90 | for { 91 | // try each known server. 92 | for _, srv := range ck.servers { 93 | args := &LeaveArgs{} 94 | args.GID = gid 95 | var reply LeaveReply 96 | ok := call(srv, "ShardMaster.Leave", args, &reply) 97 | if ok { 98 | return 99 | } 100 | } 101 | time.Sleep(100 * time.Millisecond) 102 | } 103 | } 104 | 105 | func (ck *Clerk) Move(shard int, gid int64) { 106 | for { 107 | // try each known server. 108 | for _, srv := range ck.servers { 109 | args := &MoveArgs{} 110 | args.Shard = shard 111 | args.GID = gid 112 | var reply MoveReply 113 | ok := call(srv, "ShardMaster.Move", args, &reply) 114 | if ok { 115 | return 116 | } 117 | } 118 | time.Sleep(100 * time.Millisecond) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /6.824-2015/src/shardmaster/common.go: -------------------------------------------------------------------------------- 1 | package shardmaster 2 | 3 | // 4 | // Master shard server: assigns shards to replication groups. 5 | // 6 | // RPC interface: 7 | // Join(gid, servers) -- replica group gid is joining, give it some shards. 8 | // Leave(gid) -- replica group gid is retiring, hand off all its shards. 9 | // Move(shard, gid) -- hand off one shard from current owner to gid. 10 | // Query(num) -> fetch Config # num, or latest config if num==-1. 11 | // 12 | // A Config (configuration) describes a set of replica groups, and the 13 | // replica group responsible for each shard. Configs are numbered. Config 14 | // #0 is the initial configuration, with no groups and all shards 15 | // assigned to group 0 (the invalid group). 16 | // 17 | // A GID is a replica group ID. GIDs must be uniqe and > 0. 18 | // Once a GID joins, and leaves, it should never join again. 19 | // 20 | // Please don't change this file. 21 | // 22 | 23 | const NShards = 10 24 | 25 | type Config struct { 26 | Num int // config number 27 | Shards [NShards]int64 // shard -> gid 28 | Groups map[int64][]string // gid -> servers[] 29 | } 30 | 31 | type JoinArgs struct { 32 | GID int64 // unique replica group ID 33 | Servers []string // group server ports 34 | } 35 | 36 | type JoinReply struct { 37 | } 38 | 39 | type LeaveArgs struct { 40 | GID int64 41 | } 42 | 43 | type LeaveReply struct { 44 | } 45 | 46 | type MoveArgs struct { 47 | Shard int 48 | GID int64 49 | } 50 | 51 | type MoveReply struct { 52 | } 53 | 54 | type QueryArgs struct { 55 | Num int // desired config number 56 | } 57 | 58 | type QueryReply struct { 59 | Config Config 60 | } 61 | -------------------------------------------------------------------------------- /6.824-2015/src/viewservice/client.go: -------------------------------------------------------------------------------- 1 | package viewservice 2 | 3 | import "net/rpc" 4 | import "fmt" 5 | 6 | // 7 | // the viewservice Clerk lives in the client 8 | // and maintains a little state. 9 | // 10 | type Clerk struct { 11 | me string // client's name (host:port) 12 | server string // viewservice's host:port 13 | } 14 | 15 | func MakeClerk(me string, server string) *Clerk { 16 | ck := new(Clerk) 17 | ck.me = me 18 | ck.server = server 19 | return ck 20 | } 21 | 22 | // 23 | // call() sends an RPC to the rpcname handler on server srv 24 | // with arguments args, waits for the reply, and leaves the 25 | // reply in reply. the reply argument should be a pointer 26 | // to a reply structure. 27 | // 28 | // the return value is true if the server responded, and false 29 | // if call() was not able to contact the server. in particular, 30 | // the reply's contents are only valid if call() returned true. 31 | // 32 | // you should assume that call() will return an 33 | // error after a while if the server is dead. 34 | // don't provide your own time-out mechanism. 35 | // 36 | // please use call() to send all RPCs, in client.go and server.go. 37 | // please don't change this function. 38 | // 39 | func call(srv string, rpcname string, 40 | args interface{}, reply interface{}) bool { 41 | c, errx := rpc.Dial("unix", srv) 42 | if errx != nil { 43 | return false 44 | } 45 | defer c.Close() 46 | 47 | err := c.Call(rpcname, args, reply) 48 | if err == nil { 49 | return true 50 | } 51 | 52 | fmt.Println(err) 53 | return false 54 | } 55 | 56 | func (ck *Clerk) Ping(viewnum uint) (View, error) { 57 | // prepare the arguments. 58 | args := &PingArgs{} 59 | args.Me = ck.me 60 | args.Viewnum = viewnum 61 | var reply PingReply 62 | 63 | // send an RPC request, wait for the reply. 64 | ok := call(ck.server, "ViewServer.Ping", args, &reply) 65 | if ok == false { 66 | return View{}, fmt.Errorf("Ping(%v) failed", viewnum) 67 | } 68 | 69 | return reply.View, nil 70 | } 71 | 72 | func (ck *Clerk) Get() (View, bool) { 73 | args := &GetArgs{} 74 | var reply GetReply 75 | ok := call(ck.server, "ViewServer.Get", args, &reply) 76 | if ok == false { 77 | return View{}, false 78 | } 79 | return reply.View, true 80 | } 81 | 82 | func (ck *Clerk) Primary() string { 83 | v, ok := ck.Get() 84 | if ok { 85 | return v.Primary 86 | } 87 | return "" 88 | } 89 | -------------------------------------------------------------------------------- /6.824-2015/src/viewservice/common.go: -------------------------------------------------------------------------------- 1 | package viewservice 2 | 3 | import "time" 4 | 5 | // 6 | // This is a non-replicated view service for a simple 7 | // primary/backup system. 8 | // 9 | // The view service goes through a sequence of numbered 10 | // views, each with a primary and (if possible) a backup. 11 | // A view consists of a view number and the host:port of 12 | // the view's primary and backup p/b servers. 13 | // 14 | // The primary in a view is always either the primary 15 | // or the backup of the previous view (in order to ensure 16 | // that the p/b service's state is preserved). 17 | // 18 | // Each p/b server should send a Ping RPC once per PingInterval. 19 | // The view server replies with a description of the current 20 | // view. The Pings let the view server know that the p/b 21 | // server is still alive; inform the p/b server of the current 22 | // view; and inform the view server of the most recent view 23 | // that the p/b server knows about. 24 | // 25 | // The view server proceeds to a new view when either it hasn't 26 | // received a ping from the primary or backup for a while, or 27 | // if there was no backup and a new server starts Pinging. 28 | // 29 | // The view server will not proceed to a new view until 30 | // the primary from the current view acknowledges 31 | // that it is operating in the current view. This helps 32 | // ensure that there's at most one p/b primary operating at 33 | // a time. 34 | // 35 | 36 | type View struct { 37 | Viewnum uint 38 | Primary string 39 | Backup string 40 | } 41 | 42 | // clients should send a Ping RPC this often, 43 | // to tell the viewservice that the client is alive. 44 | const PingInterval = time.Millisecond * 100 45 | 46 | // the viewserver will declare a client dead if it misses 47 | // this many Ping RPCs in a row. 48 | const DeadPings = 5 49 | 50 | // 51 | // Ping(): called by a primary/backup server to tell the 52 | // view service it is alive, to indicate whether p/b server 53 | // has seen the latest view, and for p/b server to learn 54 | // the latest view. 55 | // 56 | // If Viewnum is zero, the caller is signalling that it is 57 | // alive and could become backup if needed. 58 | // 59 | 60 | type PingArgs struct { 61 | Me string // "host:port" 62 | Viewnum uint // caller's notion of current view # 63 | } 64 | 65 | type PingReply struct { 66 | View View 67 | } 68 | 69 | // 70 | // Get(): fetch the current view, without volunteering 71 | // to be a server. mostly for clients of the p/b service, 72 | // and for testing. 73 | // 74 | 75 | type GetArgs struct { 76 | } 77 | 78 | type GetReply struct { 79 | View View 80 | } 81 | -------------------------------------------------------------------------------- /6.824-2015/src/viewservice/server.go: -------------------------------------------------------------------------------- 1 | package viewservice 2 | 3 | import "net" 4 | import "net/rpc" 5 | import "log" 6 | import "time" 7 | import "sync" 8 | import "fmt" 9 | import "os" 10 | import "sync/atomic" 11 | 12 | type ViewServer struct { 13 | mu sync.Mutex 14 | l net.Listener 15 | dead int32 // for testing 16 | rpccount int32 // for testing 17 | me string 18 | 19 | // Your declarations here. 20 | currentview View 21 | recenttime map[string]time.Time 22 | ack bool 23 | } 24 | 25 | func (vs *ViewServer) promote() { 26 | vs.currentview.Primary = vs.currentview.Backup 27 | vs.currentview.Backup = "" 28 | vs.currentview.Viewnum += 1 29 | vs.ack = false 30 | } 31 | 32 | func (vs *ViewServer) removeBackup() { 33 | vs.currentview.Backup = "" 34 | vs.ack = false 35 | } 36 | 37 | func (vs *ViewServer) acceptPrimary(primary string) { 38 | vs.currentview.Primary = primary 39 | vs.currentview.Viewnum += 1 40 | vs.ack = false 41 | } 42 | 43 | func (vs *ViewServer) acceptBackup(backup string) { 44 | vs.currentview.Backup = backup 45 | vs.currentview.Viewnum += 1 46 | vs.ack = false 47 | } 48 | 49 | // 50 | // server Ping RPC handler. 51 | // 52 | func (vs *ViewServer) Ping(args *PingArgs, reply *PingReply) error { 53 | 54 | // Your code here. 55 | vs.mu.Lock() 56 | defer vs.mu.Unlock() 57 | 58 | switch args.Me { 59 | case vs.currentview.Primary: 60 | if args.Viewnum == vs.currentview.Viewnum { 61 | vs.ack = true 62 | vs.recenttime[vs.currentview.Primary] = time.Now() 63 | } else { 64 | if vs.ack { 65 | vs.promote() 66 | } 67 | } 68 | case vs.currentview.Backup: 69 | if args.Viewnum == vs.currentview.Viewnum { 70 | vs.recenttime[vs.currentview.Backup] = time.Now() 71 | } else { 72 | if vs.ack { 73 | vs.removeBackup() 74 | } 75 | } 76 | default: 77 | if vs.currentview.Primary == "" { 78 | vs.acceptPrimary(args.Me) 79 | } else if vs.currentview.Backup == "" && vs.ack { 80 | vs.acceptBackup(args.Me) 81 | } 82 | } 83 | 84 | reply.View = vs.currentview 85 | 86 | return nil 87 | } 88 | 89 | // 90 | // server Get() RPC handler. 91 | // 92 | func (vs *ViewServer) Get(args *GetArgs, reply *GetReply) error { 93 | 94 | // Your code here. 95 | vs.mu.Lock() 96 | defer vs.mu.Unlock() 97 | 98 | reply.View = vs.currentview 99 | 100 | return nil 101 | } 102 | 103 | // 104 | // tick() is called once per PingInterval; it should notice 105 | // if servers have died or recovered, and change the view 106 | // accordingly. 107 | // 108 | func (vs *ViewServer) tick() { 109 | 110 | // Your code here. 111 | 112 | vs.mu.Lock() 113 | defer vs.mu.Unlock() 114 | 115 | if vs.ack == false { 116 | return 117 | } 118 | 119 | t1 := time.Now() 120 | if vs.currentview.Primary != "" { 121 | t2 := vs.recenttime[vs.currentview.Primary] 122 | 123 | if t1.Sub(t2) > DeadPings*PingInterval { 124 | vs.promote() 125 | } 126 | } 127 | 128 | if vs.currentview.Backup != "" { 129 | t2 := vs.recenttime[vs.currentview.Backup] 130 | 131 | if t1.Sub(t2) > DeadPings*PingInterval { 132 | vs.removeBackup() 133 | } 134 | } 135 | } 136 | 137 | // 138 | // tell the server to shut itself down. 139 | // for testing. 140 | // please don't change these two functions. 141 | // 142 | func (vs *ViewServer) Kill() { 143 | atomic.StoreInt32(&vs.dead, 1) 144 | vs.l.Close() 145 | } 146 | 147 | // 148 | // has this server been asked to shut down? 149 | // 150 | func (vs *ViewServer) isdead() bool { 151 | return atomic.LoadInt32(&vs.dead) != 0 152 | } 153 | 154 | // please don't change this function. 155 | func (vs *ViewServer) GetRPCCount() int32 { 156 | return atomic.LoadInt32(&vs.rpccount) 157 | } 158 | 159 | func StartServer(me string) *ViewServer { 160 | vs := new(ViewServer) 161 | vs.me = me 162 | // Your vs.* initializations here. 163 | vs.recenttime = make(map[string]time.Time) 164 | vs.ack = false 165 | vs.currentview = View{} 166 | 167 | // tell net/rpc about our RPC server and handlers. 168 | rpcs := rpc.NewServer() 169 | rpcs.Register(vs) 170 | 171 | // prepare to receive connections from clients. 172 | // change "unix" to "tcp" to use over a network. 173 | os.Remove(vs.me) // only needed for "unix" 174 | l, e := net.Listen("unix", vs.me) 175 | if e != nil { 176 | log.Fatal("listen error: ", e) 177 | } 178 | vs.l = l 179 | 180 | // please don't change any of the following code, 181 | // or do anything to subvert it. 182 | 183 | // create a thread to accept RPC connections from clients. 184 | go func() { 185 | for vs.isdead() == false { 186 | conn, err := vs.l.Accept() 187 | if err == nil && vs.isdead() == false { 188 | atomic.AddInt32(&vs.rpccount, 1) 189 | go rpcs.ServeConn(conn) 190 | } else if err == nil { 191 | conn.Close() 192 | } 193 | if err != nil && vs.isdead() == false { 194 | fmt.Printf("ViewServer(%v) accept: %v\n", me, err.Error()) 195 | vs.Kill() 196 | } 197 | } 198 | }() 199 | 200 | // create a thread to call tick() periodically. 201 | go func() { 202 | for vs.isdead() == false { 203 | vs.tick() 204 | time.Sleep(PingInterval) 205 | } 206 | }() 207 | 208 | return vs 209 | } 210 | -------------------------------------------------------------------------------- /6.824-2015/src/viewservice/test_test.go: -------------------------------------------------------------------------------- 1 | package viewservice 2 | 3 | import "testing" 4 | import "runtime" 5 | import "time" 6 | import "fmt" 7 | import "os" 8 | import "strconv" 9 | 10 | func check(t *testing.T, ck *Clerk, p string, b string, n uint) { 11 | view, _ := ck.Get() 12 | if view.Primary != p { 13 | t.Fatalf("wanted primary %v, got %v", p, view.Primary) 14 | } 15 | if view.Backup != b { 16 | t.Fatalf("wanted backup %v, got %v", b, view.Backup) 17 | } 18 | if n != 0 && n != view.Viewnum { 19 | t.Fatalf("wanted viewnum %v, got %v", n, view.Viewnum) 20 | } 21 | if ck.Primary() != p { 22 | t.Fatalf("wanted primary %v, got %v", p, ck.Primary()) 23 | } 24 | } 25 | 26 | func port(suffix string) string { 27 | s := "/var/tmp/824-" 28 | s += strconv.Itoa(os.Getuid()) + "/" 29 | os.Mkdir(s, 0777) 30 | s += "viewserver-" 31 | s += strconv.Itoa(os.Getpid()) + "-" 32 | s += suffix 33 | return s 34 | } 35 | 36 | func Test1(t *testing.T) { 37 | runtime.GOMAXPROCS(4) 38 | 39 | vshost := port("v") 40 | vs := StartServer(vshost) 41 | 42 | ck1 := MakeClerk(port("1"), vshost) 43 | ck2 := MakeClerk(port("2"), vshost) 44 | ck3 := MakeClerk(port("3"), vshost) 45 | 46 | // 47 | 48 | if ck1.Primary() != "" { 49 | t.Fatalf("there was a primary too soon") 50 | } 51 | 52 | // very first primary 53 | fmt.Printf("Test: First primary ...\n") 54 | 55 | for i := 0; i < DeadPings*2; i++ { 56 | view, _ := ck1.Ping(0) 57 | if view.Primary == ck1.me { 58 | break 59 | } 60 | time.Sleep(PingInterval) 61 | } 62 | check(t, ck1, ck1.me, "", 1) 63 | fmt.Printf(" ... Passed\n") 64 | 65 | // very first backup 66 | fmt.Printf("Test: First backup ...\n") 67 | 68 | { 69 | vx, _ := ck1.Get() 70 | for i := 0; i < DeadPings*2; i++ { 71 | ck1.Ping(1) 72 | view, _ := ck2.Ping(0) 73 | if view.Backup == ck2.me { 74 | break 75 | } 76 | time.Sleep(PingInterval) 77 | } 78 | check(t, ck1, ck1.me, ck2.me, vx.Viewnum+1) 79 | } 80 | fmt.Printf(" ... Passed\n") 81 | 82 | // primary dies, backup should take over 83 | fmt.Printf("Test: Backup takes over if primary fails ...\n") 84 | 85 | { 86 | ck1.Ping(2) 87 | vx, _ := ck2.Ping(2) 88 | for i := 0; i < DeadPings*2; i++ { 89 | v, _ := ck2.Ping(vx.Viewnum) 90 | if v.Primary == ck2.me && v.Backup == "" { 91 | break 92 | } 93 | time.Sleep(PingInterval) 94 | } 95 | check(t, ck2, ck2.me, "", vx.Viewnum+1) 96 | } 97 | fmt.Printf(" ... Passed\n") 98 | 99 | // revive ck1, should become backup 100 | fmt.Printf("Test: Restarted server becomes backup ...\n") 101 | 102 | { 103 | vx, _ := ck2.Get() 104 | ck2.Ping(vx.Viewnum) 105 | for i := 0; i < DeadPings*2; i++ { 106 | ck1.Ping(0) 107 | v, _ := ck2.Ping(vx.Viewnum) 108 | if v.Primary == ck2.me && v.Backup == ck1.me { 109 | break 110 | } 111 | time.Sleep(PingInterval) 112 | } 113 | check(t, ck2, ck2.me, ck1.me, vx.Viewnum+1) 114 | } 115 | fmt.Printf(" ... Passed\n") 116 | 117 | // start ck3, kill the primary (ck2), the previous backup (ck1) 118 | // should become the server, and ck3 the backup. 119 | // this should happen in a single view change, without 120 | // any period in which there's no backup. 121 | fmt.Printf("Test: Idle third server becomes backup if primary fails ...\n") 122 | 123 | { 124 | vx, _ := ck2.Get() 125 | ck2.Ping(vx.Viewnum) 126 | for i := 0; i < DeadPings*2; i++ { 127 | ck3.Ping(0) 128 | v, _ := ck1.Ping(vx.Viewnum) 129 | if v.Primary == ck1.me && v.Backup == ck3.me { 130 | break 131 | } 132 | vx = v 133 | time.Sleep(PingInterval) 134 | } 135 | check(t, ck1, ck1.me, ck3.me, vx.Viewnum+1) 136 | } 137 | fmt.Printf(" ... Passed\n") 138 | 139 | // kill and immediately restart the primary -- does viewservice 140 | // conclude primary is down even though it's pinging? 141 | fmt.Printf("Test: Restarted primary treated as dead ...\n") 142 | 143 | { 144 | vx, _ := ck1.Get() 145 | ck1.Ping(vx.Viewnum) 146 | for i := 0; i < DeadPings*2; i++ { 147 | ck1.Ping(0) 148 | ck3.Ping(vx.Viewnum) 149 | v, _ := ck3.Get() 150 | if v.Primary != ck1.me { 151 | break 152 | } 153 | time.Sleep(PingInterval) 154 | } 155 | vy, _ := ck3.Get() 156 | if vy.Primary != ck3.me { 157 | t.Fatalf("expected primary=%v, got %v\n", ck3.me, vy.Primary) 158 | } 159 | } 160 | fmt.Printf(" ... Passed\n") 161 | 162 | fmt.Printf("Test: Dead backup is removed from view ...\n") 163 | 164 | // set up a view with just 3 as primary, 165 | // to prepare for the next test. 166 | { 167 | for i := 0; i < DeadPings*3; i++ { 168 | vx, _ := ck3.Get() 169 | ck3.Ping(vx.Viewnum) 170 | time.Sleep(PingInterval) 171 | } 172 | v, _ := ck3.Get() 173 | if v.Primary != ck3.me || v.Backup != "" { 174 | t.Fatalf("wrong primary or backup") 175 | } 176 | } 177 | fmt.Printf(" ... Passed\n") 178 | 179 | // does viewserver wait for ack of previous view before 180 | // starting the next one? 181 | fmt.Printf("Test: Viewserver waits for primary to ack view ...\n") 182 | 183 | { 184 | // set up p=ck3 b=ck1, but 185 | // but do not ack 186 | vx, _ := ck1.Get() 187 | for i := 0; i < DeadPings*3; i++ { 188 | ck1.Ping(0) 189 | ck3.Ping(vx.Viewnum) 190 | v, _ := ck1.Get() 191 | if v.Viewnum > vx.Viewnum { 192 | break 193 | } 194 | time.Sleep(PingInterval) 195 | } 196 | check(t, ck1, ck3.me, ck1.me, vx.Viewnum+1) 197 | vy, _ := ck1.Get() 198 | // ck3 is the primary, but it never acked. 199 | // let ck3 die. check that ck1 is not promoted. 200 | for i := 0; i < DeadPings*3; i++ { 201 | v, _ := ck1.Ping(vy.Viewnum) 202 | if v.Viewnum > vy.Viewnum { 203 | break 204 | } 205 | time.Sleep(PingInterval) 206 | } 207 | check(t, ck2, ck3.me, ck1.me, vy.Viewnum) 208 | } 209 | fmt.Printf(" ... Passed\n") 210 | 211 | // if old servers die, check that a new (uninitialized) server 212 | // cannot take over. 213 | fmt.Printf("Test: Uninitialized server can't become primary ...\n") 214 | 215 | { 216 | for i := 0; i < DeadPings*2; i++ { 217 | v, _ := ck1.Get() 218 | ck1.Ping(v.Viewnum) 219 | ck2.Ping(0) 220 | ck3.Ping(v.Viewnum) 221 | time.Sleep(PingInterval) 222 | } 223 | for i := 0; i < DeadPings*2; i++ { 224 | ck2.Ping(0) 225 | time.Sleep(PingInterval) 226 | } 227 | vz, _ := ck2.Get() 228 | if vz.Primary == ck2.me { 229 | t.Fatalf("uninitialized backup promoted to primary") 230 | } 231 | } 232 | fmt.Printf(" ... Passed\n") 233 | 234 | vs.Kill() 235 | } 236 | -------------------------------------------------------------------------------- /6.824-2016/.gitignore: -------------------------------------------------------------------------------- 1 | pkg/ 2 | api.key 3 | *-handin.tar.gz 4 | -------------------------------------------------------------------------------- /6.824-2016/Makefile: -------------------------------------------------------------------------------- 1 | # This is the Makefile helping you submit the labs. 2 | # Just create 6.824/api.key with your API key in it, 3 | # and submit your lab with the following command: 4 | # $ make [lab1|lab2|lab3a|lab3b|lab4a|lab4b|lab5] 5 | 6 | LABS=" lab1 lab2 lab3a lab3b lab4a lab4b lab5 " 7 | 8 | %: 9 | @echo "Preparing $@-handin.tar.gz" 10 | @echo "Checking for committed temporary files..." 11 | @if git ls-files | grep -E 'mrtmp|mrinput' > /dev/null; then \ 12 | echo "" ; \ 13 | echo "OBS! You have committed some large temporary files:" ; \ 14 | echo "" ; \ 15 | git ls-files | grep -E 'mrtmp|mrinput' | sed 's/^/\t/' ; \ 16 | echo "" ; \ 17 | echo "Follow the instructions at http://stackoverflow.com/a/308684/472927" ; \ 18 | echo "to remove them, and then run make again." ; \ 19 | echo "" ; \ 20 | exit 1 ; \ 21 | fi 22 | @if echo $(LABS) | grep -q " $@ " ; then \ 23 | echo "Tarring up your submission..." ; \ 24 | tar cvzf $@-handin.tar.gz \ 25 | "--exclude=src/main/pg-*.txt" \ 26 | "--exclude=src/main/diskvd" \ 27 | "--exclude=src/mapreduce/824-mrinput-*.txt" \ 28 | "--exclude=mrtmp.*" \ 29 | "--exclude=src/main/diff.out" \ 30 | Makefile src; \ 31 | if ! test -e api.key ; then \ 32 | echo "Missing $(PWD)/api.key. Please create the file with your key in it or submit the $@-handin.tar.gz via the web interface."; \ 33 | else \ 34 | echo "Are you sure you want to submit $@? Enter 'yes' to continue:"; \ 35 | read line; \ 36 | if test "$$line" != "yes" ; then echo "Giving up submission"; exit; fi; \ 37 | if test `stat -c "%s" "$@-handin.tar.gz" 2>/dev/null || stat -f "%z" "$@-handin.tar.gz"` -ge 20971520 ; then echo "File exceeds 20MB."; exit; fi; \ 38 | mv api.key api.key.fix ; \ 39 | cat api.key.fix | tr -d '\n' > api.key ; \ 40 | rm api.key.fix ; \ 41 | curl -F file=@$@-handin.tar.gz -F "key= 0 { 74 | shard = int(key[0]) 75 | } 76 | shard %= shardmaster.NShards 77 | return shard 78 | } 79 | 80 | // 81 | // fetch the current value for a key. 82 | // returns "" if the key does not exist. 83 | // keeps trying forever in the face of all other errors. 84 | // 85 | func (ck *Clerk) Get(key string) string { 86 | ck.mu.Lock() 87 | defer ck.mu.Unlock() 88 | 89 | // You'll have to modify Get(). 90 | 91 | for { 92 | shard := key2shard(key) 93 | 94 | gid := ck.config.Shards[shard] 95 | 96 | servers, ok := ck.config.Groups[gid] 97 | 98 | if ok { 99 | // try each server in the shard's replication group. 100 | for _, srv := range servers { 101 | args := &GetArgs{} 102 | args.Key = key 103 | var reply GetReply 104 | ok := call(srv, "DisKV.Get", args, &reply) 105 | if ok && (reply.Err == OK || reply.Err == ErrNoKey) { 106 | return reply.Value 107 | } 108 | if ok && (reply.Err == ErrWrongGroup) { 109 | break 110 | } 111 | } 112 | } 113 | 114 | time.Sleep(100 * time.Millisecond) 115 | 116 | // ask master for a new configuration. 117 | ck.config = ck.sm.Query(-1) 118 | } 119 | } 120 | 121 | // send a Put or Append request. 122 | func (ck *Clerk) PutAppend(key string, value string, op string) { 123 | ck.mu.Lock() 124 | defer ck.mu.Unlock() 125 | 126 | // You'll have to modify PutAppend(). 127 | 128 | for { 129 | shard := key2shard(key) 130 | 131 | gid := ck.config.Shards[shard] 132 | 133 | servers, ok := ck.config.Groups[gid] 134 | 135 | if ok { 136 | // try each server in the shard's replication group. 137 | for _, srv := range servers { 138 | args := &PutAppendArgs{} 139 | args.Key = key 140 | args.Value = value 141 | args.Op = op 142 | var reply PutAppendReply 143 | ok := call(srv, "DisKV.PutAppend", args, &reply) 144 | if ok && reply.Err == OK { 145 | return 146 | } 147 | if ok && (reply.Err == ErrWrongGroup) { 148 | break 149 | } 150 | } 151 | } 152 | 153 | time.Sleep(100 * time.Millisecond) 154 | 155 | // ask master for a new configuration. 156 | ck.config = ck.sm.Query(-1) 157 | } 158 | } 159 | 160 | func (ck *Clerk) Put(key string, value string) { 161 | ck.PutAppend(key, value, "Put") 162 | } 163 | func (ck *Clerk) Append(key string, value string) { 164 | ck.PutAppend(key, value, "Append") 165 | } 166 | -------------------------------------------------------------------------------- /6.824-2016/src/diskv/common.go: -------------------------------------------------------------------------------- 1 | package diskv 2 | 3 | // 4 | // Sharded key/value server. 5 | // Lots of replica groups, each running op-at-a-time paxos. 6 | // Shardmaster decides which group serves each shard. 7 | // Shardmaster may change shard assignment from time to time. 8 | // 9 | // You will have to modify these definitions. 10 | // 11 | 12 | const ( 13 | OK = "OK" 14 | ErrNoKey = "ErrNoKey" 15 | ErrWrongGroup = "ErrWrongGroup" 16 | ) 17 | 18 | type Err string 19 | 20 | type PutAppendArgs struct { 21 | Key string 22 | Value string 23 | Op string // "Put" or "Append" 24 | // You'll have to add definitions here. 25 | // Field names must start with capital letters, 26 | // otherwise RPC will break. 27 | 28 | } 29 | 30 | type PutAppendReply struct { 31 | Err Err 32 | } 33 | 34 | type GetArgs struct { 35 | Key string 36 | // You'll have to add definitions here. 37 | } 38 | 39 | type GetReply struct { 40 | Err Err 41 | Value string 42 | } 43 | -------------------------------------------------------------------------------- /6.824-2016/src/kvpaxos/client.go: -------------------------------------------------------------------------------- 1 | package kvpaxos 2 | 3 | import "net/rpc" 4 | import "crypto/rand" 5 | import "math/big" 6 | 7 | import "fmt" 8 | 9 | type Clerk struct { 10 | servers []string 11 | // You will have to modify this struct. 12 | } 13 | 14 | func nrand() int64 { 15 | max := big.NewInt(int64(1) << 62) 16 | bigx, _ := rand.Int(rand.Reader, max) 17 | x := bigx.Int64() 18 | return x 19 | } 20 | 21 | func MakeClerk(servers []string) *Clerk { 22 | ck := new(Clerk) 23 | ck.servers = servers 24 | // You'll have to add code here. 25 | return ck 26 | } 27 | 28 | // 29 | // call() sends an RPC to the rpcname handler on server srv 30 | // with arguments args, waits for the reply, and leaves the 31 | // reply in reply. the reply argument should be a pointer 32 | // to a reply structure. 33 | // 34 | // the return value is true if the server responded, and false 35 | // if call() was not able to contact the server. in particular, 36 | // the reply's contents are only valid if call() returned true. 37 | // 38 | // you should assume that call() will return an 39 | // error after a while if the server is dead. 40 | // don't provide your own time-out mechanism. 41 | // 42 | // please use call() to send all RPCs, in client.go and server.go. 43 | // please don't change this function. 44 | // 45 | func call(srv string, rpcname string, 46 | args interface{}, reply interface{}) bool { 47 | c, errx := rpc.Dial("unix", srv) 48 | if errx != nil { 49 | return false 50 | } 51 | defer c.Close() 52 | 53 | err := c.Call(rpcname, args, reply) 54 | if err == nil { 55 | return true 56 | } 57 | 58 | fmt.Println(err) 59 | return false 60 | } 61 | 62 | // 63 | // fetch the current value for a key. 64 | // returns "" if the key does not exist. 65 | // keeps trying forever in the face of all other errors. 66 | // 67 | func (ck *Clerk) Get(key string) string { 68 | // You will have to modify this function. 69 | return "" 70 | } 71 | 72 | // 73 | // shared by Put and Append. 74 | // 75 | func (ck *Clerk) PutAppend(key string, value string, op string) { 76 | // You will have to modify this function. 77 | } 78 | 79 | func (ck *Clerk) Put(key string, value string) { 80 | ck.PutAppend(key, value, "Put") 81 | } 82 | func (ck *Clerk) Append(key string, value string) { 83 | ck.PutAppend(key, value, "Append") 84 | } 85 | -------------------------------------------------------------------------------- /6.824-2016/src/kvpaxos/common.go: -------------------------------------------------------------------------------- 1 | package kvpaxos 2 | 3 | const ( 4 | OK = "OK" 5 | ErrNoKey = "ErrNoKey" 6 | ) 7 | 8 | type Err string 9 | 10 | // Put or Append 11 | type PutAppendArgs struct { 12 | // You'll have to add definitions here. 13 | Key string 14 | Value string 15 | Op string // "Put" or "Append" 16 | // You'll have to add definitions here. 17 | // Field names must start with capital letters, 18 | // otherwise RPC will break. 19 | } 20 | 21 | type PutAppendReply struct { 22 | Err Err 23 | } 24 | 25 | type GetArgs struct { 26 | Key string 27 | // You'll have to add definitions here. 28 | } 29 | 30 | type GetReply struct { 31 | Err Err 32 | Value string 33 | } 34 | -------------------------------------------------------------------------------- /6.824-2016/src/kvpaxos/server.go: -------------------------------------------------------------------------------- 1 | package kvpaxos 2 | 3 | import "net" 4 | import "fmt" 5 | import "net/rpc" 6 | import "log" 7 | import "paxos" 8 | import "sync" 9 | import "sync/atomic" 10 | import "os" 11 | import "syscall" 12 | import "encoding/gob" 13 | import "math/rand" 14 | 15 | const Debug = 0 16 | 17 | func DPrintf(format string, a ...interface{}) (n int, err error) { 18 | if Debug > 0 { 19 | log.Printf(format, a...) 20 | } 21 | return 22 | } 23 | 24 | type Op struct { 25 | // Your definitions here. 26 | // Field names must start with capital letters, 27 | // otherwise RPC will break. 28 | } 29 | 30 | type KVPaxos struct { 31 | mu sync.Mutex 32 | l net.Listener 33 | me int 34 | dead int32 // for testing 35 | unreliable int32 // for testing 36 | px *paxos.Paxos 37 | 38 | // Your definitions here. 39 | } 40 | 41 | func (kv *KVPaxos) Get(args *GetArgs, reply *GetReply) error { 42 | // Your code here. 43 | return nil 44 | } 45 | 46 | func (kv *KVPaxos) PutAppend(args *PutAppendArgs, reply *PutAppendReply) error { 47 | // Your code here. 48 | 49 | return nil 50 | } 51 | 52 | // tell the server to shut itself down. 53 | // please do not change these two functions. 54 | func (kv *KVPaxos) kill() { 55 | DPrintf("Kill(%d): die\n", kv.me) 56 | atomic.StoreInt32(&kv.dead, 1) 57 | kv.l.Close() 58 | kv.px.Kill() 59 | } 60 | 61 | // call this to find out if the server is dead. 62 | func (kv *KVPaxos) isdead() bool { 63 | return atomic.LoadInt32(&kv.dead) != 0 64 | } 65 | 66 | // please do not change these two functions. 67 | func (kv *KVPaxos) setunreliable(what bool) { 68 | if what { 69 | atomic.StoreInt32(&kv.unreliable, 1) 70 | } else { 71 | atomic.StoreInt32(&kv.unreliable, 0) 72 | } 73 | } 74 | 75 | func (kv *KVPaxos) isunreliable() bool { 76 | return atomic.LoadInt32(&kv.unreliable) != 0 77 | } 78 | 79 | // 80 | // servers[] contains the ports of the set of 81 | // servers that will cooperate via Paxos to 82 | // form the fault-tolerant key/value service. 83 | // me is the index of the current server in servers[]. 84 | // 85 | func StartServer(servers []string, me int) *KVPaxos { 86 | // call gob.Register on structures you want 87 | // Go's RPC library to marshall/unmarshall. 88 | gob.Register(Op{}) 89 | 90 | kv := new(KVPaxos) 91 | kv.me = me 92 | 93 | // Your initialization code here. 94 | 95 | rpcs := rpc.NewServer() 96 | rpcs.Register(kv) 97 | 98 | kv.px = paxos.Make(servers, me, rpcs) 99 | 100 | os.Remove(servers[me]) 101 | l, e := net.Listen("unix", servers[me]) 102 | if e != nil { 103 | log.Fatal("listen error: ", e) 104 | } 105 | kv.l = l 106 | 107 | // please do not change any of the following code, 108 | // or do anything to subvert it. 109 | 110 | go func() { 111 | for kv.isdead() == false { 112 | conn, err := kv.l.Accept() 113 | if err == nil && kv.isdead() == false { 114 | if kv.isunreliable() && (rand.Int63()%1000) < 100 { 115 | // discard the request. 116 | conn.Close() 117 | } else if kv.isunreliable() && (rand.Int63()%1000) < 200 { 118 | // process the request but force discard of reply. 119 | c1 := conn.(*net.UnixConn) 120 | f, _ := c1.File() 121 | err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR) 122 | if err != nil { 123 | fmt.Printf("shutdown: %v\n", err) 124 | } 125 | go rpcs.ServeConn(conn) 126 | } else { 127 | go rpcs.ServeConn(conn) 128 | } 129 | } else if err == nil { 130 | conn.Close() 131 | } 132 | if err != nil && kv.isdead() == false { 133 | fmt.Printf("KVPaxos(%v) accept: %v\n", me, err.Error()) 134 | kv.kill() 135 | } 136 | } 137 | }() 138 | 139 | return kv 140 | } 141 | -------------------------------------------------------------------------------- /6.824-2016/src/kvraft/client.go: -------------------------------------------------------------------------------- 1 | package raftkv 2 | 3 | import "labrpc" 4 | import "crypto/rand" 5 | import "math/big" 6 | import "sync" 7 | 8 | type Clerk struct { 9 | servers []*labrpc.ClientEnd 10 | // You will have to modify this struct. 11 | id int64 12 | seq int 13 | mu sync.Mutex 14 | } 15 | 16 | func nrand() int64 { 17 | max := big.NewInt(int64(1) << 62) 18 | bigx, _ := rand.Int(rand.Reader, max) 19 | x := bigx.Int64() 20 | return x 21 | } 22 | 23 | func MakeClerk(servers []*labrpc.ClientEnd) *Clerk { 24 | ck := new(Clerk) 25 | ck.servers = servers 26 | // You'll have to add code here. 27 | ck.id = nrand() 28 | ck.seq = 0 29 | 30 | return ck 31 | } 32 | 33 | // 34 | // fetch the current value for a key. 35 | // returns "" if the key does not exist. 36 | // keeps trying forever in the face of all other errors. 37 | // 38 | // you can send an RPC with code like this: 39 | // ok := ck.servers[i].Call("RaftKV.Get", &args, &reply) 40 | // 41 | // the types of args and reply (including whether they are pointers) 42 | // must match the declared types of the RPC handler function's 43 | // arguments. and reply must be passed as a pointer. 44 | // 45 | func (ck *Clerk) Get(key string) string { 46 | // You will have to modify this function. 47 | i := 0 48 | var args GetArgs 49 | args.Key = key 50 | args.Id = ck.id 51 | ck.mu.Lock() 52 | args.Seq = ck.seq 53 | ck.seq++ 54 | ck.mu.Unlock() 55 | for { 56 | var reply GetReply 57 | ok := ck.servers[i].Call("RaftKV.Get", &args, &reply) 58 | if !ok || reply.WrongLeader { 59 | i = (i + 1) % len(ck.servers) 60 | continue 61 | } 62 | 63 | if reply.Err == ErrNoKey { 64 | return "" 65 | } 66 | 67 | return reply.Value 68 | } 69 | } 70 | 71 | // 72 | // shared by Put and Append. 73 | // 74 | // you can send an RPC with code like this: 75 | // ok := ck.servers[i].Call("RaftKV.PutAppend", &args, &reply) 76 | // 77 | // the types of args and reply (including whether they are pointers) 78 | // must match the declared types of the RPC handler function's 79 | // arguments. and reply must be passed as a pointer. 80 | // 81 | func (ck *Clerk) PutAppend(key string, value string, op string) { 82 | // You will have to modify this function. 83 | i := 0 84 | var args PutAppendArgs 85 | args.Key = key 86 | args.Value = value 87 | args.Op = op 88 | args.Id = ck.id 89 | ck.mu.Lock() 90 | args.Seq = ck.seq 91 | ck.seq++ 92 | ck.mu.Unlock() 93 | for { 94 | var reply PutAppendReply 95 | ok := ck.servers[i].Call("RaftKV.PutAppend", &args, &reply) 96 | if !ok || reply.WrongLeader { 97 | i = (i + 1) % len(ck.servers) 98 | continue 99 | } 100 | 101 | return 102 | } 103 | } 104 | 105 | func (ck *Clerk) Put(key string, value string) { 106 | ck.PutAppend(key, value, "Put") 107 | } 108 | func (ck *Clerk) Append(key string, value string) { 109 | ck.PutAppend(key, value, "Append") 110 | } 111 | -------------------------------------------------------------------------------- /6.824-2016/src/kvraft/common.go: -------------------------------------------------------------------------------- 1 | package raftkv 2 | 3 | const ( 4 | OK = "OK" 5 | ErrNoKey = "ErrNoKey" 6 | 7 | Get = "Get" 8 | Put = "Put" 9 | Append = "Append" 10 | ) 11 | 12 | type Err string 13 | 14 | // Put or Append 15 | type PutAppendArgs struct { 16 | // You'll have to add definitions here. 17 | Key string 18 | Value string 19 | Op string // "Put" or "Append" 20 | // You'll have to add definitions here. 21 | // Field names must start with capital letters, 22 | // otherwise RPC will break. 23 | Id int64 24 | Seq int 25 | } 26 | 27 | type PutAppendReply struct { 28 | WrongLeader bool 29 | Err Err 30 | } 31 | 32 | type GetArgs struct { 33 | Key string 34 | // You'll have to add definitions here. 35 | Id int64 36 | Seq int 37 | } 38 | 39 | type GetReply struct { 40 | WrongLeader bool 41 | Err Err 42 | Value string 43 | } 44 | -------------------------------------------------------------------------------- /6.824-2016/src/kvraft/server.go: -------------------------------------------------------------------------------- 1 | package raftkv 2 | 3 | import ( 4 | "bytes" 5 | "encoding/gob" 6 | "labrpc" 7 | "log" 8 | "raft" 9 | "sync" 10 | "time" 11 | ) 12 | 13 | const Debug = 0 14 | 15 | func DPrintf(format string, a ...interface{}) (n int, err error) { 16 | if Debug > 0 { 17 | log.Printf(format, a...) 18 | } 19 | return 20 | } 21 | 22 | type Op struct { 23 | // Your definitions here. 24 | // Field names must start with capital letters, 25 | // otherwise RPC will break. 26 | Op string 27 | Key string 28 | Value string 29 | Seq int 30 | Id int64 31 | } 32 | 33 | type RaftKV struct { 34 | mu sync.Mutex 35 | me int 36 | rf *raft.Raft 37 | applyCh chan raft.ApplyMsg 38 | 39 | maxraftstate int // snapshot if log grows this big 40 | 41 | // Your definitions here. 42 | database map[string]string 43 | result map[int]chan Op 44 | ack map[int64]int 45 | } 46 | 47 | func (kv *RaftKV) Get(args *GetArgs, reply *GetReply) { 48 | // Your code here. 49 | var op Op 50 | op.Op = Get 51 | op.Key = args.Key 52 | op.Id = args.Id 53 | op.Seq = args.Seq 54 | 55 | index, _, isLeader := kv.rf.Start(op) 56 | reply.WrongLeader = !isLeader 57 | if !isLeader { 58 | return 59 | } 60 | 61 | ok := kv.checkOpCommitted(index, op) 62 | if ok { 63 | reply.WrongLeader = false 64 | kv.mu.Lock() 65 | v, ok := kv.database[args.Key] 66 | if ok { 67 | reply.Value = v 68 | reply.Err = OK 69 | kv.ack[op.Id] = op.Seq 70 | } else { 71 | reply.Err = ErrNoKey 72 | } 73 | kv.mu.Unlock() 74 | } else { 75 | reply.WrongLeader = true 76 | } 77 | } 78 | 79 | func (kv *RaftKV) PutAppend(args *PutAppendArgs, reply *PutAppendReply) { 80 | // Your code here. 81 | var op Op 82 | op.Op = args.Op 83 | op.Key = args.Key 84 | op.Value = args.Value 85 | op.Id = args.Id 86 | op.Seq = args.Seq 87 | 88 | index, _, isLeader := kv.rf.Start(op) 89 | reply.WrongLeader = !isLeader 90 | if !isLeader { 91 | return 92 | } 93 | 94 | ok := kv.checkOpCommitted(index, op) 95 | if ok { 96 | reply.WrongLeader = false 97 | reply.Err = OK 98 | } else { 99 | reply.WrongLeader = true 100 | } 101 | } 102 | 103 | func (kv *RaftKV) checkOpCommitted(index int, op Op) bool { 104 | kv.mu.Lock() 105 | ch, ok := kv.result[index] 106 | if !ok { 107 | ch = make(chan Op, 1) 108 | kv.result[index] = ch 109 | } 110 | kv.mu.Unlock() 111 | 112 | select { 113 | case o := <-ch: 114 | committed := o == op 115 | if committed { 116 | DPrintf("kvraft %d: Commit %d %v", kv.me, index, op) 117 | } 118 | return committed 119 | case <-time.After(time.Duration(2000 * time.Millisecond)): 120 | DPrintf("kvraft %d: Timeout %d %v", kv.me, index, op) 121 | return false 122 | } 123 | } 124 | 125 | func (kv *RaftKV) excute(op Op) { 126 | switch op.Op { 127 | case Put: 128 | kv.database[op.Key] = op.Value 129 | case Append: 130 | v, ok := kv.database[op.Key] 131 | if ok { 132 | kv.database[op.Key] = v + op.Value 133 | } else { 134 | kv.database[op.Key] = op.Value 135 | } 136 | case Get: 137 | return 138 | default: 139 | } 140 | kv.ack[op.Id] = op.Seq 141 | } 142 | 143 | func (kv *RaftKV) checkDuplicate(op Op) bool { 144 | v, ok := kv.ack[op.Id] 145 | if ok { 146 | return v >= op.Seq 147 | } 148 | 149 | return false 150 | } 151 | 152 | func (kv *RaftKV) startSnapshot(index int) { 153 | w := new(bytes.Buffer) 154 | e := gob.NewEncoder(w) 155 | e.Encode(kv.database) 156 | e.Encode(kv.ack) 157 | data := w.Bytes() 158 | go kv.rf.StartSnapshot(data, index) 159 | } 160 | 161 | func (kv *RaftKV) readSnapshot(data []byte) { 162 | if data == nil || len(data) == 0 { 163 | return 164 | } 165 | 166 | r := bytes.NewBuffer(data) 167 | d := gob.NewDecoder(r) 168 | 169 | var lastIncludeIndex int 170 | var lastIncludeTerm int 171 | 172 | d.Decode(&lastIncludeIndex) 173 | d.Decode(&lastIncludeTerm) 174 | 175 | d.Decode(&kv.database) 176 | d.Decode(&kv.ack) 177 | } 178 | 179 | func (kv *RaftKV) apply() { 180 | for { 181 | msg := <-kv.applyCh 182 | if msg.UseSnapshot { 183 | kv.mu.Lock() 184 | kv.readSnapshot(msg.Snapshot) 185 | kv.mu.Unlock() 186 | } else { 187 | kv.mu.Lock() 188 | op := msg.Command.(Op) 189 | index := msg.Index 190 | if !kv.checkDuplicate(op) { 191 | kv.excute(op) 192 | } 193 | ch, ok := kv.result[index] 194 | if ok { 195 | select { 196 | case <-kv.result[index]: 197 | default: 198 | } 199 | ch <- op 200 | } else { 201 | kv.result[index] = make(chan Op, 1) 202 | } 203 | 204 | if kv.maxraftstate != -1 && kv.rf.GetRaftStateSize() > kv.maxraftstate { 205 | DPrintf("kvraft %d: Start snapshot %d %d", kv.me, index, kv.rf.GetRaftStateSize()) 206 | kv.startSnapshot(index) 207 | } 208 | kv.mu.Unlock() 209 | } 210 | } 211 | } 212 | 213 | // 214 | // the tester calls Kill() when a RaftKV instance won't 215 | // be needed again. you are not required to do anything 216 | // in Kill(), but it might be convenient to (for example) 217 | // turn off debug output from this instance. 218 | // 219 | func (kv *RaftKV) Kill() { 220 | kv.rf.Kill() 221 | // Your code here, if desired. 222 | } 223 | 224 | // 225 | // servers[] contains the ports of the set of 226 | // servers that will cooperate via Raft to 227 | // form the fault-tolerant key/value service. 228 | // me is the index of the current server in servers[]. 229 | // the k/v server should store snapshots with persister.SaveSnapshot(), 230 | // and Raft should save its state (including log) with persister.SaveRaftState(). 231 | // the k/v server should snapshot when Raft's saved state exceeds maxraftstate bytes, 232 | // in order to allow Raft to garbage-collect its log. if maxraftstate is -1, 233 | // you don't need to snapshot. 234 | // StartKVServer() must return quickly, so it should start goroutines 235 | // for any long-running work. 236 | // 237 | func StartKVServer(servers []*labrpc.ClientEnd, me int, persister *raft.Persister, maxraftstate int) *RaftKV { 238 | // call gob.Register on structures you want 239 | // Go's RPC library to marshall/unmarshall. 240 | gob.Register(Op{}) 241 | 242 | kv := new(RaftKV) 243 | kv.me = me 244 | kv.maxraftstate = maxraftstate 245 | 246 | // Your initialization code here. 247 | 248 | kv.applyCh = make(chan raft.ApplyMsg) 249 | kv.rf = raft.Make(servers, me, persister, kv.applyCh) 250 | kv.result = make(map[int]chan Op) 251 | kv.database = make(map[string]string) 252 | kv.ack = make(map[int64]int) 253 | 254 | go kv.apply() 255 | 256 | return kv 257 | } 258 | -------------------------------------------------------------------------------- /6.824-2016/src/lockservice/client.go: -------------------------------------------------------------------------------- 1 | package lockservice 2 | 3 | import "net/rpc" 4 | import "fmt" 5 | 6 | // 7 | // the lockservice Clerk lives in the client 8 | // and maintains a little state. 9 | // 10 | type Clerk struct { 11 | servers [2]string // primary port, backup port 12 | // Your definitions here. 13 | } 14 | 15 | func MakeClerk(primary string, backup string) *Clerk { 16 | ck := new(Clerk) 17 | ck.servers[0] = primary 18 | ck.servers[1] = backup 19 | // Your initialization code here. 20 | return ck 21 | } 22 | 23 | // 24 | // call() sends an RPC to the rpcname handler on server srv 25 | // with arguments args, waits for the reply, and leaves the 26 | // reply in reply. the reply argument should be the address 27 | // of a reply structure. 28 | // 29 | // call() returns true if the server responded, and false 30 | // if call() was not able to contact the server. in particular, 31 | // reply's contents are valid if and only if call() returned true. 32 | // 33 | // you should assume that call() will return an 34 | // error after a while if the server is dead. 35 | // don't provide your own time-out mechanism. 36 | // 37 | // please use call() to send all RPCs, in client.go and server.go. 38 | // please don't change this function. 39 | // 40 | func call(srv string, rpcname string, 41 | args interface{}, reply interface{}) bool { 42 | c, errx := rpc.Dial("unix", srv) 43 | if errx != nil { 44 | return false 45 | } 46 | defer c.Close() 47 | 48 | err := c.Call(rpcname, args, reply) 49 | if err == nil { 50 | return true 51 | } 52 | 53 | fmt.Println(err) 54 | return false 55 | } 56 | 57 | // 58 | // ask the lock service for a lock. 59 | // returns true if the lock service 60 | // granted the lock, false otherwise. 61 | // 62 | // you will have to modify this function. 63 | // 64 | func (ck *Clerk) Lock(lockname string) bool { 65 | // prepare the arguments. 66 | args := &LockArgs{} 67 | args.Lockname = lockname 68 | var reply LockReply 69 | 70 | // send an RPC request, wait for the reply. 71 | ok := call(ck.servers[0], "LockServer.Lock", args, &reply) 72 | if ok == false { 73 | return false 74 | } 75 | 76 | return reply.OK 77 | } 78 | 79 | // 80 | // ask the lock service to unlock a lock. 81 | // returns true if the lock was previously held, 82 | // false otherwise. 83 | // 84 | 85 | func (ck *Clerk) Unlock(lockname string) bool { 86 | 87 | // Your code here. 88 | 89 | return false 90 | } 91 | -------------------------------------------------------------------------------- /6.824-2016/src/lockservice/common.go: -------------------------------------------------------------------------------- 1 | package lockservice 2 | 3 | // 4 | // RPC definitions for a simple lock service. 5 | // 6 | // You will need to modify this file. 7 | // 8 | 9 | // 10 | // Lock(lockname) returns OK=true if the lock is not held. 11 | // If it is held, it returns OK=false immediately. 12 | // 13 | type LockArgs struct { 14 | // Go's net/rpc requires that these field 15 | // names start with upper case letters! 16 | Lockname string // lock name 17 | } 18 | 19 | type LockReply struct { 20 | OK bool 21 | } 22 | 23 | // 24 | // Unlock(lockname) returns OK=true if the lock was held. 25 | // It returns OK=false if the lock was not held. 26 | // 27 | type UnlockArgs struct { 28 | Lockname string 29 | } 30 | 31 | type UnlockReply struct { 32 | OK bool 33 | } 34 | -------------------------------------------------------------------------------- /6.824-2016/src/lockservice/server.go: -------------------------------------------------------------------------------- 1 | package lockservice 2 | 3 | import "net" 4 | import "net/rpc" 5 | import "log" 6 | import "sync" 7 | import "fmt" 8 | import "os" 9 | import "io" 10 | import "time" 11 | 12 | type LockServer struct { 13 | mu sync.Mutex 14 | l net.Listener 15 | dead bool // for test_test.go 16 | dying bool // for test_test.go 17 | 18 | am_primary bool // am I the primary? 19 | backup string // backup's port 20 | 21 | // for each lock name, is it locked? 22 | locks map[string]bool 23 | } 24 | 25 | // 26 | // server Lock RPC handler. 27 | // 28 | // you will have to modify this function 29 | // 30 | func (ls *LockServer) Lock(args *LockArgs, reply *LockReply) error { 31 | ls.mu.Lock() 32 | defer ls.mu.Unlock() 33 | 34 | locked, _ := ls.locks[args.Lockname] 35 | 36 | if locked { 37 | reply.OK = false 38 | } else { 39 | reply.OK = true 40 | ls.locks[args.Lockname] = true 41 | } 42 | 43 | return nil 44 | } 45 | 46 | // 47 | // server Unlock RPC handler. 48 | // 49 | func (ls *LockServer) Unlock(args *UnlockArgs, reply *UnlockReply) error { 50 | 51 | // Your code here. 52 | 53 | return nil 54 | } 55 | 56 | // 57 | // tell the server to shut itself down. 58 | // for testing. 59 | // please don't change this. 60 | // 61 | func (ls *LockServer) kill() { 62 | ls.dead = true 63 | ls.l.Close() 64 | } 65 | 66 | // 67 | // hack to allow test_test.go to have primary process 68 | // an RPC but not send a reply. can't use the shutdown() 69 | // trick b/c that causes client to immediately get an 70 | // error and send to backup before primary does. 71 | // please don't change anything to do with DeafConn. 72 | // 73 | type DeafConn struct { 74 | c io.ReadWriteCloser 75 | } 76 | 77 | func (dc DeafConn) Write(p []byte) (n int, err error) { 78 | return len(p), nil 79 | } 80 | func (dc DeafConn) Close() error { 81 | return dc.c.Close() 82 | } 83 | func (dc DeafConn) Read(p []byte) (n int, err error) { 84 | return dc.c.Read(p) 85 | } 86 | 87 | func StartServer(primary string, backup string, am_primary bool) *LockServer { 88 | ls := new(LockServer) 89 | ls.backup = backup 90 | ls.am_primary = am_primary 91 | ls.locks = map[string]bool{} 92 | 93 | // Your initialization code here. 94 | 95 | me := "" 96 | if am_primary { 97 | me = primary 98 | } else { 99 | me = backup 100 | } 101 | 102 | // tell net/rpc about our RPC server and handlers. 103 | rpcs := rpc.NewServer() 104 | rpcs.Register(ls) 105 | 106 | // prepare to receive connections from clients. 107 | // change "unix" to "tcp" to use over a network. 108 | os.Remove(me) // only needed for "unix" 109 | l, e := net.Listen("unix", me) 110 | if e != nil { 111 | log.Fatal("listen error: ", e) 112 | } 113 | ls.l = l 114 | 115 | // please don't change any of the following code, 116 | // or do anything to subvert it. 117 | 118 | // create a thread to accept RPC connections from clients. 119 | go func() { 120 | for ls.dead == false { 121 | conn, err := ls.l.Accept() 122 | if err == nil && ls.dead == false { 123 | if ls.dying { 124 | // process the request but force discard of reply. 125 | 126 | // without this the connection is never closed, 127 | // b/c ServeConn() is waiting for more requests. 128 | // test_test.go depends on this two seconds. 129 | go func() { 130 | time.Sleep(2 * time.Second) 131 | conn.Close() 132 | }() 133 | ls.l.Close() 134 | 135 | // this object has the type ServeConn expects, 136 | // but discards writes (i.e. discards the RPC reply). 137 | deaf_conn := DeafConn{c: conn} 138 | 139 | rpcs.ServeConn(deaf_conn) 140 | 141 | ls.dead = true 142 | } else { 143 | go rpcs.ServeConn(conn) 144 | } 145 | } else if err == nil { 146 | conn.Close() 147 | } 148 | if err != nil && ls.dead == false { 149 | fmt.Printf("LockServer(%v) accept: %v\n", me, err.Error()) 150 | ls.kill() 151 | } 152 | } 153 | }() 154 | 155 | return ls 156 | } 157 | -------------------------------------------------------------------------------- /6.824-2016/src/main/diskvd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ts25504/mit-courses/77ea5a2f6a6fd4f5f6491b5315e8210361887e6b/6.824-2016/src/main/diskvd -------------------------------------------------------------------------------- /6.824-2016/src/main/diskvd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 4 | // start a diskvd server. it's a member of some replica 5 | // group, which has other members, and it needs to know 6 | // how to talk to the members of the shardmaster service. 7 | // used by ../diskv/test_test.go 8 | // 9 | // arguments: 10 | // -g groupid 11 | // -m masterport1 -m masterport2 ... 12 | // -s replicaport1 -s replicaport2 ... 13 | // -i my-index-in-server-port-list 14 | // -u unreliable 15 | // -d directory 16 | // -r restart 17 | 18 | import "time" 19 | import "diskv" 20 | import "os" 21 | import "fmt" 22 | import "strconv" 23 | import "runtime" 24 | 25 | func usage() { 26 | fmt.Printf("Usage: diskvd -g gid -m master... -s server... -i my-index -d dir\n") 27 | os.Exit(1) 28 | } 29 | 30 | func main() { 31 | var gid int64 = -1 // my replica group ID 32 | masters := []string{} // ports of shardmasters 33 | replicas := []string{} // ports of servers in my replica group 34 | me := -1 // my index in replicas[] 35 | unreliable := false 36 | dir := "" // store persistent data here 37 | restart := false 38 | 39 | for i := 1; i+1 < len(os.Args); i += 2 { 40 | a0 := os.Args[i] 41 | a1 := os.Args[i+1] 42 | if a0 == "-g" { 43 | gid, _ = strconv.ParseInt(a1, 10, 64) 44 | } else if a0 == "-m" { 45 | masters = append(masters, a1) 46 | } else if a0 == "-s" { 47 | replicas = append(replicas, a1) 48 | } else if a0 == "-i" { 49 | me, _ = strconv.Atoi(a1) 50 | } else if a0 == "-u" { 51 | unreliable, _ = strconv.ParseBool(a1) 52 | } else if a0 == "-d" { 53 | dir = a1 54 | } else if a0 == "-r" { 55 | restart, _ = strconv.ParseBool(a1) 56 | } else { 57 | usage() 58 | } 59 | } 60 | 61 | if gid < 0 || me < 0 || len(masters) < 1 || me >= len(replicas) || dir == "" { 62 | usage() 63 | } 64 | 65 | runtime.GOMAXPROCS(4) 66 | 67 | srv := diskv.StartServer(gid, masters, replicas, me, dir, restart) 68 | srv.Setunreliable(unreliable) 69 | 70 | // for safety, force quit after 10 minutes. 71 | time.Sleep(10 * 60 * time.Second) 72 | mep, _ := os.FindProcess(os.Getpid()) 73 | mep.Kill() 74 | } 75 | -------------------------------------------------------------------------------- /6.824-2016/src/main/ii.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "os" 4 | import "fmt" 5 | import "mapreduce" 6 | import "strings" 7 | import "unicode" 8 | import "strconv" 9 | import "sort" 10 | 11 | // The mapping function is called once for each piece of the input. 12 | // In this framework, the key is the name of the file that is being processed, 13 | // and the value is the file's contents. The return value should be a slice of 14 | // key/value pairs, each represented by a mapreduce.KeyValue. 15 | func mapF(document string, value string) (res []mapreduce.KeyValue) { 16 | // TODO: you should complete this to do the inverted index challenge 17 | f := func(c rune) bool { 18 | return !unicode.IsLetter(c) 19 | } 20 | 21 | words := strings.FieldsFunc(value, f) 22 | for _, w := range words { 23 | kv := mapreduce.KeyValue{w, document} 24 | res = append(res, kv) 25 | } 26 | return 27 | } 28 | 29 | // The reduce function is called once for each key generated by Map, with a 30 | // list of that key's string value (merged across all inputs). The return value 31 | // should be a single output value for that key. 32 | func reduceF(key string, values []string) string { 33 | // TODO: you should complete this to do the inverted index challenge 34 | fileMap := make(map[string]int) 35 | for _, s := range values { 36 | fileMap[s] = 1 37 | } 38 | 39 | files := make([]string, len(fileMap)) 40 | 41 | i := 0 42 | for k, _ := range fileMap { 43 | files[i] = k 44 | i++ 45 | } 46 | 47 | value := strconv.Itoa(len(files)) + " " 48 | 49 | sort.Strings(files) 50 | 51 | for i, f := range files { 52 | value += f 53 | if i != len(files)-1 { 54 | value += "," 55 | } 56 | } 57 | 58 | return value 59 | } 60 | 61 | // Can be run in 3 ways: 62 | // 1) Sequential (e.g., go run wc.go master sequential x1.txt .. xN.txt) 63 | // 2) Master (e.g., go run wc.go master localhost:7777 x1.txt .. xN.txt) 64 | // 3) Worker (e.g., go run wc.go worker localhost:7777 localhost:7778 &) 65 | func main() { 66 | if len(os.Args) < 4 { 67 | fmt.Printf("%s: see usage comments in file\n", os.Args[0]) 68 | } else if os.Args[1] == "master" { 69 | var mr *mapreduce.Master 70 | if os.Args[2] == "sequential" { 71 | mr = mapreduce.Sequential("iiseq", os.Args[3:], 3, mapF, reduceF) 72 | } else { 73 | mr = mapreduce.Distributed("iiseq", os.Args[3:], 3, os.Args[2]) 74 | } 75 | mr.Wait() 76 | } else { 77 | mapreduce.RunWorker(os.Args[2], os.Args[3], mapF, reduceF, 100) 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /6.824-2016/src/main/lockc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 4 | // see comments in lockd.go 5 | // 6 | 7 | import "lockservice" 8 | import "os" 9 | import "fmt" 10 | 11 | func usage() { 12 | fmt.Printf("Usage: lockc -l|-u primaryport backupport lockname\n") 13 | os.Exit(1) 14 | } 15 | 16 | func main() { 17 | if len(os.Args) == 5 { 18 | ck := lockservice.MakeClerk(os.Args[2], os.Args[3]) 19 | var ok bool 20 | if os.Args[1] == "-l" { 21 | ok = ck.Lock(os.Args[4]) 22 | } else if os.Args[1] == "-u" { 23 | ok = ck.Unlock(os.Args[4]) 24 | } else { 25 | usage() 26 | } 27 | fmt.Printf("reply: %v\n", ok) 28 | } else { 29 | usage() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /6.824-2016/src/main/lockd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // export GOPATH=~/6.824 4 | // go build lockd.go 5 | // go build lockc.go 6 | // ./lockd -p a b & 7 | // ./lockd -b a b & 8 | // ./lockc -l a b lx 9 | // ./lockc -u a b lx 10 | // 11 | // on Athena, use /tmp/myname-a and /tmp/myname-b 12 | // instead of a and b. 13 | 14 | import "time" 15 | import "lockservice" 16 | import "os" 17 | import "fmt" 18 | 19 | func main() { 20 | if len(os.Args) == 4 && os.Args[1] == "-p" { 21 | lockservice.StartServer(os.Args[2], os.Args[3], true) 22 | } else if len(os.Args) == 4 && os.Args[1] == "-b" { 23 | lockservice.StartServer(os.Args[2], os.Args[3], false) 24 | } else { 25 | fmt.Printf("Usage: lockd -p|-b primaryport backupport\n") 26 | os.Exit(1) 27 | } 28 | for { 29 | time.Sleep(100 * time.Second) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /6.824-2016/src/main/mr-challenge.txt: -------------------------------------------------------------------------------- 1 | women: 15 pg-being_ernest.txt,pg-dorian_gray.txt,pg-dracula.txt,pg-emma.txt,pg-frankenstein.txt,pg-great_expectations.txt,pg-huckleberry_finn.txt,pg-les_miserables.txt,pg-metamorphosis.txt,pg-moby_dick.txt,pg-sherlock_holmes.txt,pg-tale_of_two_cities.txt,pg-tom_sawyer.txt,pg-ulysses.txt,pg-war_and_peace.txt 2 | won: 15 pg-being_ernest.txt,pg-dorian_gray.txt,pg-dracula.txt,pg-frankenstein.txt,pg-great_expectations.txt,pg-grimm.txt,pg-huckleberry_finn.txt,pg-les_miserables.txt,pg-metamorphosis.txt,pg-moby_dick.txt,pg-sherlock_holmes.txt,pg-tale_of_two_cities.txt,pg-tom_sawyer.txt,pg-ulysses.txt,pg-war_and_peace.txt 3 | wonderful: 15 pg-being_ernest.txt,pg-dorian_gray.txt,pg-dracula.txt,pg-emma.txt,pg-frankenstein.txt,pg-great_expectations.txt,pg-grimm.txt,pg-huckleberry_finn.txt,pg-les_miserables.txt,pg-moby_dick.txt,pg-sherlock_holmes.txt,pg-tale_of_two_cities.txt,pg-tom_sawyer.txt,pg-ulysses.txt,pg-war_and_peace.txt 4 | words: 15 pg-dorian_gray.txt,pg-dracula.txt,pg-emma.txt,pg-frankenstein.txt,pg-great_expectations.txt,pg-grimm.txt,pg-huckleberry_finn.txt,pg-les_miserables.txt,pg-metamorphosis.txt,pg-moby_dick.txt,pg-sherlock_holmes.txt,pg-tale_of_two_cities.txt,pg-tom_sawyer.txt,pg-ulysses.txt,pg-war_and_peace.txt 5 | worked: 15 pg-dorian_gray.txt,pg-dracula.txt,pg-emma.txt,pg-frankenstein.txt,pg-great_expectations.txt,pg-grimm.txt,pg-huckleberry_finn.txt,pg-les_miserables.txt,pg-metamorphosis.txt,pg-moby_dick.txt,pg-sherlock_holmes.txt,pg-tale_of_two_cities.txt,pg-tom_sawyer.txt,pg-ulysses.txt,pg-war_and_peace.txt 6 | worse: 15 pg-being_ernest.txt,pg-dorian_gray.txt,pg-dracula.txt,pg-emma.txt,pg-frankenstein.txt,pg-great_expectations.txt,pg-grimm.txt,pg-huckleberry_finn.txt,pg-les_miserables.txt,pg-moby_dick.txt,pg-sherlock_holmes.txt,pg-tale_of_two_cities.txt,pg-tom_sawyer.txt,pg-ulysses.txt,pg-war_and_peace.txt 7 | wounded: 15 pg-being_ernest.txt,pg-dorian_gray.txt,pg-dracula.txt,pg-emma.txt,pg-frankenstein.txt,pg-great_expectations.txt,pg-grimm.txt,pg-huckleberry_finn.txt,pg-les_miserables.txt,pg-moby_dick.txt,pg-sherlock_holmes.txt,pg-tale_of_two_cities.txt,pg-tom_sawyer.txt,pg-ulysses.txt,pg-war_and_peace.txt 8 | yes: 15 pg-being_ernest.txt,pg-dorian_gray.txt,pg-dracula.txt,pg-emma.txt,pg-great_expectations.txt,pg-grimm.txt,pg-huckleberry_finn.txt,pg-les_miserables.txt,pg-metamorphosis.txt,pg-moby_dick.txt,pg-sherlock_holmes.txt,pg-tale_of_two_cities.txt,pg-tom_sawyer.txt,pg-ulysses.txt,pg-war_and_peace.txt 9 | younger: 15 pg-being_ernest.txt,pg-dorian_gray.txt,pg-dracula.txt,pg-emma.txt,pg-frankenstein.txt,pg-great_expectations.txt,pg-grimm.txt,pg-huckleberry_finn.txt,pg-les_miserables.txt,pg-moby_dick.txt,pg-sherlock_holmes.txt,pg-tale_of_two_cities.txt,pg-tom_sawyer.txt,pg-ulysses.txt,pg-war_and_peace.txt 10 | yours: 15 pg-being_ernest.txt,pg-dorian_gray.txt,pg-dracula.txt,pg-emma.txt,pg-frankenstein.txt,pg-great_expectations.txt,pg-grimm.txt,pg-huckleberry_finn.txt,pg-les_miserables.txt,pg-moby_dick.txt,pg-sherlock_holmes.txt,pg-tale_of_two_cities.txt,pg-tom_sawyer.txt,pg-ulysses.txt,pg-war_and_peace.txt 11 | -------------------------------------------------------------------------------- /6.824-2016/src/main/mr-testout.txt: -------------------------------------------------------------------------------- 1 | he: 34077 2 | was: 37044 3 | that: 37495 4 | I: 44502 5 | in: 46092 6 | a: 60558 7 | to: 74357 8 | of: 79727 9 | and: 93990 10 | the: 154024 11 | -------------------------------------------------------------------------------- /6.824-2016/src/main/pbc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 4 | // pbservice client application 5 | // 6 | // export GOPATH=~/6.824 7 | // go build viewd.go 8 | // go build pbd.go 9 | // go build pbc.go 10 | // ./viewd /tmp/rtm-v & 11 | // ./pbd /tmp/rtm-v /tmp/rtm-1 & 12 | // ./pbd /tmp/rtm-v /tmp/rtm-2 & 13 | // ./pbc /tmp/rtm-v key1 value1 14 | // ./pbc /tmp/rtm-v key1 15 | // 16 | // change "rtm" to your user name. 17 | // start the pbd programs in separate windows and kill 18 | // and restart them to exercise fault tolerance. 19 | // 20 | 21 | import "pbservice" 22 | import "os" 23 | import "fmt" 24 | 25 | func usage() { 26 | fmt.Printf("Usage: pbc viewport key\n") 27 | fmt.Printf(" pbc viewport key value\n") 28 | os.Exit(1) 29 | } 30 | 31 | func main() { 32 | if len(os.Args) == 3 { 33 | // get 34 | ck := pbservice.MakeClerk(os.Args[1], "") 35 | v := ck.Get(os.Args[2]) 36 | fmt.Printf("%v\n", v) 37 | } else if len(os.Args) == 4 { 38 | // put 39 | ck := pbservice.MakeClerk(os.Args[1], "") 40 | ck.Put(os.Args[2], os.Args[3]) 41 | } else { 42 | usage() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /6.824-2016/src/main/pbd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 4 | // see directions in pbc.go 5 | // 6 | 7 | import "time" 8 | import "pbservice" 9 | import "os" 10 | import "fmt" 11 | 12 | func main() { 13 | if len(os.Args) != 3 { 14 | fmt.Printf("Usage: pbd viewport myport\n") 15 | os.Exit(1) 16 | } 17 | 18 | pbservice.StartServer(os.Args[1], os.Args[2]) 19 | 20 | for { 21 | time.Sleep(100 * time.Second) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /6.824-2016/src/main/test-ii.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | go run ii.go master sequential pg-*.txt 3 | sort -k1,1 mrtmp.iiseq | sort -snk2,2 | grep -v '16' | tail -10 | diff - mr-challenge.txt > diff.out 4 | if [ -s diff.out ] 5 | then 6 | echo "Failed test. Output should be as in mr-challenge.txt. Your output differs as follows (from diff.out):" > /dev/stderr 7 | cat diff.out 8 | else 9 | echo "Passed test" > /dev/stderr 10 | fi 11 | 12 | -------------------------------------------------------------------------------- /6.824-2016/src/main/test-mr.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | here=$(dirname "$0") 3 | [[ "$here" = /* ]] || here="$PWD/$here" 4 | export GOPATH="$here/../../" 5 | echo "" 6 | echo "==> Part I" 7 | go test -run Sequential mapreduce/... 8 | echo "" 9 | echo "==> Part II" 10 | (cd "$here" && ./test-wc.sh > /dev/null) 11 | echo "" 12 | echo "==> Part III" 13 | go test -run TestBasic mapreduce/... 14 | echo "" 15 | echo "==> Part IV" 16 | go test -run Failure mapreduce/... 17 | echo "" 18 | echo "==> Part V (challenge)" 19 | (cd "$here" && ./test-ii.sh > /dev/null) 20 | 21 | rm "$here"/mrtmp.* "$here"/diff.out 22 | -------------------------------------------------------------------------------- /6.824-2016/src/main/test-wc.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | go run wc.go master sequential pg-*.txt 3 | sort -n -k2 mrtmp.wcseq | tail -10 | diff - mr-testout.txt > diff.out 4 | if [ -s diff.out ] 5 | then 6 | echo "Failed test. Output should be as in mr-testout.txt. Your output differs as follows (from diff.out):" > /dev/stderr 7 | cat diff.out 8 | else 9 | echo "Passed test" > /dev/stderr 10 | fi 11 | 12 | -------------------------------------------------------------------------------- /6.824-2016/src/main/viewd.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // 4 | // see directions in pbc.go 5 | // 6 | 7 | import "time" 8 | import "viewservice" 9 | import "os" 10 | import "fmt" 11 | 12 | func main() { 13 | if len(os.Args) != 2 { 14 | fmt.Printf("Usage: viewd port\n") 15 | os.Exit(1) 16 | } 17 | 18 | viewservice.StartServer(os.Args[1]) 19 | 20 | for { 21 | time.Sleep(100 * time.Second) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /6.824-2016/src/main/wc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "mapreduce" 6 | "os" 7 | "strconv" 8 | "strings" 9 | "unicode" 10 | ) 11 | 12 | // The mapping function is called once for each piece of the input. 13 | // In this framework, the key is the name of the file that is being processed, 14 | // and the value is the file's contents. The return value should be a slice of 15 | // key/value pairs, each represented by a mapreduce.KeyValue. 16 | func mapF(document string, value string) (res []mapreduce.KeyValue) { 17 | // TODO: you have to write this function 18 | f := func(c rune) bool { 19 | return !unicode.IsLetter(c) 20 | } 21 | 22 | words := strings.FieldsFunc(value, f) 23 | for _, w := range words { 24 | kv := mapreduce.KeyValue{w, "1"} 25 | res = append(res, kv) 26 | } 27 | return 28 | } 29 | 30 | // The reduce function is called once for each key generated by Map, with a 31 | // list of that key's string value (merged across all inputs). The return value 32 | // should be a single output value for that key. 33 | func reduceF(key string, values []string) string { 34 | // TODO: you also have to write this function 35 | return strconv.Itoa(len(values)) 36 | } 37 | 38 | // Can be run in 3 ways: 39 | // 1) Sequential (e.g., go run wc.go master sequential x1.txt .. xN.txt) 40 | // 2) Master (e.g., go run wc.go master localhost:7777 x1.txt .. xN.txt) 41 | // 3) Worker (e.g., go run wc.go worker localhost:7777 localhost:7778 &) 42 | func main() { 43 | if len(os.Args) < 4 { 44 | fmt.Printf("%s: see usage comments in file\n", os.Args[0]) 45 | } else if os.Args[1] == "master" { 46 | var mr *mapreduce.Master 47 | if os.Args[2] == "sequential" { 48 | mr = mapreduce.Sequential("wcseq", os.Args[3:], 3, mapF, reduceF) 49 | } else { 50 | mr = mapreduce.Distributed("wcseq", os.Args[3:], 3, os.Args[2]) 51 | } 52 | mr.Wait() 53 | } else { 54 | mapreduce.RunWorker(os.Args[2], os.Args[3], mapF, reduceF, 100) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /6.824-2016/src/mapreduce/common.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | // Debugging enabled? 9 | const debugEnabled = false 10 | 11 | // DPrintf will only print if the debugEnabled const has been set to true 12 | func debug(format string, a ...interface{}) (n int, err error) { 13 | if debugEnabled { 14 | n, err = fmt.Printf(format, a...) 15 | } 16 | return 17 | } 18 | 19 | // jobPhase indicates whether a task is scheduled as a map or reduce task. 20 | type jobPhase string 21 | 22 | const ( 23 | mapPhase jobPhase = "Map" 24 | reducePhase = "Reduce" 25 | ) 26 | 27 | // KeyValue is a type used to hold the key/value pairs passed to the map and 28 | // reduce functions. 29 | type KeyValue struct { 30 | Key string 31 | Value string 32 | } 33 | 34 | // reduceName constructs the name of the intermediate file which map task 35 | // produces for reduce task . 36 | func reduceName(jobName string, mapTask int, reduceTask int) string { 37 | return "mrtmp." + jobName + "-" + strconv.Itoa(mapTask) + "-" + strconv.Itoa(reduceTask) 38 | } 39 | 40 | // mergeName constructs the name of the output file of reduce task 41 | func mergeName(jobName string, reduceTask int) string { 42 | return "mrtmp." + jobName + "-res-" + strconv.Itoa(reduceTask) 43 | } 44 | -------------------------------------------------------------------------------- /6.824-2016/src/mapreduce/common_map.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import ( 4 | "encoding/json" 5 | "hash/fnv" 6 | "log" 7 | "os" 8 | ) 9 | 10 | // doMap does the job of a map worker: it reads one of the input files 11 | // (inFile), calls the user-defined map function (mapF) for that file's 12 | // contents, and partitions the output into nReduce intermediate files. 13 | func doMap( 14 | jobName string, // the name of the MapReduce job 15 | mapTaskNumber int, // which map task this is 16 | inFile string, 17 | nReduce int, // the number of reduce task that will be run ("R" in the paper) 18 | mapF func(file string, contents string) []KeyValue, 19 | ) { 20 | // TODO: 21 | // You will need to write this function. 22 | // You can find the filename for this map task's input to reduce task number 23 | // r using reduceName(jobName, mapTaskNumber, r). The ihash function (given 24 | // below doMap) should be used to decide which file a given key belongs into. 25 | // 26 | // The intermediate output of a map task is stored in the file 27 | // system as multiple files whose name indicates which map task produced 28 | // them, as well as which reduce task they are for. Coming up with a 29 | // scheme for how to store the key/value pairs on disk can be tricky, 30 | // especially when taking into account that both keys and values could 31 | // contain newlines, quotes, and any other character you can think of. 32 | // 33 | // One format often used for serializing data to a byte stream that the 34 | // other end can correctly reconstruct is JSON. You are not required to 35 | // use JSON, but as the output of the reduce tasks *must* be JSON, 36 | // familiarizing yourself with it here may prove useful. You can write 37 | // out a data structure as a JSON string to a file using the commented 38 | // code below. The corresponding decoding functions can be found in 39 | // common_reduce.go. 40 | // 41 | // enc := json.NewEncoder(file) 42 | // for _, kv := ... { 43 | // err := enc.Encode(&kv) 44 | // 45 | // Remember to close the file after you have written all the values! 46 | 47 | debug("Map: read %s\n", inFile) 48 | file, err := os.Open(inFile) 49 | if err != nil { 50 | log.Fatal("Map: ", err) 51 | } 52 | 53 | info, err := file.Stat() 54 | if err != nil { 55 | log.Fatal("Map: ", err) 56 | } 57 | 58 | buffer := make([]byte, info.Size()) 59 | _, err = file.Read(buffer) 60 | if err != nil { 61 | log.Fatal("Map: ", err) 62 | } 63 | 64 | file.Close() 65 | 66 | kvs := mapF(inFile, string(buffer)) 67 | 68 | for i := 0; i < nReduce; i++ { 69 | fileName := reduceName(jobName, mapTaskNumber, i) 70 | debug("Map: write %s\n", fileName) 71 | file, err = os.Create(fileName) 72 | if err != nil { 73 | log.Fatal("Map: ", err) 74 | } 75 | 76 | enc := json.NewEncoder(file) 77 | for _, kv := range kvs { 78 | if ihash(kv.Key)%uint32(nReduce) == uint32(i) { 79 | err = enc.Encode(&kv) 80 | if err != nil { 81 | log.Fatal("Map: ", err) 82 | } 83 | } 84 | } 85 | 86 | file.Close() 87 | } 88 | } 89 | 90 | func ihash(s string) uint32 { 91 | h := fnv.New32a() 92 | h.Write([]byte(s)) 93 | return h.Sum32() 94 | } 95 | -------------------------------------------------------------------------------- /6.824-2016/src/mapreduce/common_reduce.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import ( 4 | "encoding/json" 5 | "log" 6 | "os" 7 | ) 8 | 9 | // doReduce does the job of a reduce worker: it reads the intermediate 10 | // key/value pairs (produced by the map phase) for this task, sorts the 11 | // intermediate key/value pairs by key, calls the user-defined reduce function 12 | // (reduceF) for each key, and writes the output to disk. 13 | func doReduce( 14 | jobName string, // the name of the whole MapReduce job 15 | reduceTaskNumber int, // which reduce task this is 16 | nMap int, // the number of map tasks that were run ("M" in the paper) 17 | reduceF func(key string, values []string) string, 18 | ) { 19 | // TODO: 20 | // You will need to write this function. 21 | // You can find the intermediate file for this reduce task from map task number 22 | // m using reduceName(jobName, m, reduceTaskNumber). 23 | // Remember that you've encoded the values in the intermediate files, so you 24 | // will need to decode them. If you chose to use JSON, you can read out 25 | // multiple decoded values by creating a decoder, and then repeatedly calling 26 | // .Decode() on it until Decode() returns an error. 27 | // 28 | // You should write the reduced output in as JSON encoded KeyValue 29 | // objects to a file named mergeName(jobName, reduceTaskNumber). We require 30 | // you to use JSON here because that is what the merger than combines the 31 | // output from all the reduce tasks expects. There is nothing "special" about 32 | // JSON -- it is just the marshalling format we chose to use. It will look 33 | // something like this: 34 | // 35 | // enc := json.NewEncoder(mergeFile) 36 | // for key in ... { 37 | // enc.Encode(KeyValue{key, reduceF(...)}) 38 | // } 39 | // file.Close() 40 | 41 | kvs := make(map[string][]string) 42 | for i := 0; i < nMap; i++ { 43 | fileName := reduceName(jobName, i, reduceTaskNumber) 44 | debug("Reduce: read %s\n", fileName) 45 | file, err := os.Open(fileName) 46 | if err != nil { 47 | log.Fatal("Reduce: ", err) 48 | } 49 | 50 | dec := json.NewDecoder(file) 51 | for { 52 | var kv KeyValue 53 | err := dec.Decode(&kv) 54 | if err != nil { 55 | break 56 | } 57 | 58 | kvs[kv.Key] = append(kvs[kv.Key], kv.Value) 59 | } 60 | 61 | file.Close() 62 | } 63 | 64 | fileName := mergeName(jobName, reduceTaskNumber) 65 | debug("Reduce: write %s\n", fileName) 66 | file, err := os.Create(fileName) 67 | if err != nil { 68 | log.Fatal("Reduce: ", err) 69 | } 70 | 71 | enc := json.NewEncoder(file) 72 | for key := range kvs { 73 | err = enc.Encode(KeyValue{key, reduceF(key, kvs[key])}) 74 | if err != nil { 75 | log.Fatal("Reduce: ", err) 76 | } 77 | } 78 | 79 | file.Close() 80 | } 81 | -------------------------------------------------------------------------------- /6.824-2016/src/mapreduce/common_rpc.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import ( 4 | "fmt" 5 | "net/rpc" 6 | ) 7 | 8 | // What follows are RPC types and methods. 9 | // Field names must start with capital letters, otherwise RPC will break. 10 | 11 | // DoTaskArgs holds the arguments that are passed to a worker when a job is 12 | // scheduled on it. 13 | type DoTaskArgs struct { 14 | JobName string 15 | File string // the file to process 16 | Phase jobPhase // are we in mapPhase or reducePhase? 17 | TaskNumber int // this task's index in the current phase 18 | 19 | // NumOtherPhase is the total number of tasks in other phase; mappers 20 | // need this to compute the number of output bins, and reducers needs 21 | // this to know how many input files to collect. 22 | NumOtherPhase int 23 | } 24 | 25 | // ShutdownReply is the response to a WorkerShutdown. 26 | // It holds the number of tasks this worker has processed since it was started. 27 | type ShutdownReply struct { 28 | Ntasks int 29 | } 30 | 31 | // RegisterArgs is the argument passed when a worker registers with the master. 32 | type RegisterArgs struct { 33 | Worker string 34 | } 35 | 36 | // call() sends an RPC to the rpcname handler on server srv 37 | // with arguments args, waits for the reply, and leaves the 38 | // reply in reply. the reply argument should be the address 39 | // of a reply structure. 40 | // 41 | // call() returns true if the server responded, and false 42 | // if call() was not able to contact the server. in particular, 43 | // reply's contents are valid if and only if call() returned true. 44 | // 45 | // you should assume that call() will time out and return an 46 | // error after a while if it doesn't get a reply from the server. 47 | // 48 | // please use call() to send all RPCs, in master.go, mapreduce.go, 49 | // and worker.go. please don't change this function. 50 | // 51 | func call(srv string, rpcname string, 52 | args interface{}, reply interface{}) bool { 53 | c, errx := rpc.Dial("unix", srv) 54 | if errx != nil { 55 | return false 56 | } 57 | defer c.Close() 58 | 59 | err := c.Call(rpcname, args, reply) 60 | if err == nil { 61 | return true 62 | } 63 | 64 | fmt.Println(err) 65 | return false 66 | } 67 | -------------------------------------------------------------------------------- /6.824-2016/src/mapreduce/master.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | "sync" 7 | ) 8 | 9 | // Master holds all the state that the master needs to keep track of. Of 10 | // particular importance is registerChannel, the channel that notifies the 11 | // master of workers that have gone idle and are in need of new work. 12 | type Master struct { 13 | sync.Mutex 14 | 15 | address string 16 | registerChannel chan string 17 | doneChannel chan bool 18 | workers []string // protected by the mutex 19 | 20 | // Per-task information 21 | jobName string // Name of currently executing job 22 | files []string // Input files 23 | nReduce int // Number of reduce partitions 24 | 25 | shutdown chan struct{} 26 | l net.Listener 27 | stats []int 28 | } 29 | 30 | // Register is an RPC method that is called by workers after they have started 31 | // up to report that they are ready to receive tasks. 32 | func (mr *Master) Register(args *RegisterArgs, _ *struct{}) error { 33 | mr.Lock() 34 | defer mr.Unlock() 35 | debug("Register: worker %s\n", args.Worker) 36 | mr.workers = append(mr.workers, args.Worker) 37 | go func() { 38 | mr.registerChannel <- args.Worker 39 | }() 40 | return nil 41 | } 42 | 43 | // newMaster initializes a new Map/Reduce Master 44 | func newMaster(master string) (mr *Master) { 45 | mr = new(Master) 46 | mr.address = master 47 | mr.shutdown = make(chan struct{}) 48 | mr.registerChannel = make(chan string) 49 | mr.doneChannel = make(chan bool) 50 | return 51 | } 52 | 53 | // Sequential runs map and reduce tasks sequentially, waiting for each task to 54 | // complete before scheduling the next. 55 | func Sequential(jobName string, files []string, nreduce int, 56 | mapF func(string, string) []KeyValue, 57 | reduceF func(string, []string) string, 58 | ) (mr *Master) { 59 | mr = newMaster("master") 60 | go mr.run(jobName, files, nreduce, func(phase jobPhase) { 61 | switch phase { 62 | case mapPhase: 63 | for i, f := range mr.files { 64 | doMap(mr.jobName, i, f, mr.nReduce, mapF) 65 | } 66 | case reducePhase: 67 | for i := 0; i < mr.nReduce; i++ { 68 | doReduce(mr.jobName, i, len(mr.files), reduceF) 69 | } 70 | } 71 | }, func() { 72 | mr.stats = []int{len(files) + nreduce} 73 | }) 74 | return 75 | } 76 | 77 | // Distributed schedules map and reduce tasks on workers that register with the 78 | // master over RPC. 79 | func Distributed(jobName string, files []string, nreduce int, master string) (mr *Master) { 80 | mr = newMaster(master) 81 | mr.startRPCServer() 82 | go mr.run(jobName, files, nreduce, mr.schedule, func() { 83 | mr.stats = mr.killWorkers() 84 | mr.stopRPCServer() 85 | }) 86 | return 87 | } 88 | 89 | // run executes a mapreduce job on the given number of mappers and reducers. 90 | // 91 | // First, it divides up the input file among the given number of mappers, and 92 | // schedules each task on workers as they become available. Each map task bins 93 | // its output in a number of bins equal to the given number of reduce tasks. 94 | // Once all the mappers have finished, workers are assigned reduce tasks. 95 | // 96 | // When all tasks have been completed, the reducer outputs are merged, 97 | // statistics are collected, and the master is shut down. 98 | // 99 | // Note that this implementation assumes a shared file system. 100 | func (mr *Master) run(jobName string, files []string, nreduce int, 101 | schedule func(phase jobPhase), 102 | finish func(), 103 | ) { 104 | mr.jobName = jobName 105 | mr.files = files 106 | mr.nReduce = nreduce 107 | 108 | fmt.Printf("%s: Starting Map/Reduce task %s\n", mr.address, mr.jobName) 109 | 110 | schedule(mapPhase) 111 | schedule(reducePhase) 112 | finish() 113 | mr.merge() 114 | 115 | fmt.Printf("%s: Map/Reduce task completed\n", mr.address) 116 | 117 | mr.doneChannel <- true 118 | } 119 | 120 | // Wait blocks until the currently scheduled work has completed. 121 | // This happens when all tasks have scheduled and completed, the final output 122 | // have been computed, and all workers have been shut down. 123 | func (mr *Master) Wait() { 124 | <-mr.doneChannel 125 | } 126 | 127 | // killWorkers cleans up all workers by sending each one a Shutdown RPC. 128 | // It also collects and returns the number of tasks each worker has performed. 129 | func (mr *Master) killWorkers() []int { 130 | mr.Lock() 131 | defer mr.Unlock() 132 | ntasks := make([]int, 0, len(mr.workers)) 133 | for _, w := range mr.workers { 134 | debug("Master: shutdown worker %s\n", w) 135 | var reply ShutdownReply 136 | ok := call(w, "Worker.Shutdown", new(struct{}), &reply) 137 | if ok == false { 138 | fmt.Printf("Master: RPC %s shutdown error\n", w) 139 | } else { 140 | ntasks = append(ntasks, reply.Ntasks) 141 | } 142 | } 143 | return ntasks 144 | } 145 | -------------------------------------------------------------------------------- /6.824-2016/src/mapreduce/master_rpc.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "net/rpc" 8 | "os" 9 | ) 10 | 11 | // Shutdown is an RPC method that shuts down the Master's RPC server. 12 | func (mr *Master) Shutdown(_, _ *struct{}) error { 13 | debug("Shutdown: registration server\n") 14 | close(mr.shutdown) 15 | mr.l.Close() // causes the Accept to fail 16 | return nil 17 | } 18 | 19 | // startRPCServer staarts the Master's RPC server. It continues accepting RPC 20 | // calls (Register in particular) for as long as the worker is alive. 21 | func (mr *Master) startRPCServer() { 22 | rpcs := rpc.NewServer() 23 | rpcs.Register(mr) 24 | os.Remove(mr.address) // only needed for "unix" 25 | l, e := net.Listen("unix", mr.address) 26 | if e != nil { 27 | log.Fatal("RegstrationServer", mr.address, " error: ", e) 28 | } 29 | mr.l = l 30 | 31 | // now that we are listening on the master address, can fork off 32 | // accepting connections to another thread. 33 | go func() { 34 | loop: 35 | for { 36 | select { 37 | case <-mr.shutdown: 38 | break loop 39 | default: 40 | } 41 | conn, err := mr.l.Accept() 42 | if err == nil { 43 | go func() { 44 | rpcs.ServeConn(conn) 45 | conn.Close() 46 | }() 47 | } else { 48 | debug("RegistrationServer: accept error", err) 49 | break 50 | } 51 | } 52 | debug("RegistrationServer: done\n") 53 | }() 54 | } 55 | 56 | // stopRPCServer stops the master RPC server. 57 | // This must be done through an RPC to avoid race conditions between the RPC 58 | // server thread and the current thread. 59 | func (mr *Master) stopRPCServer() { 60 | var reply ShutdownReply 61 | ok := call(mr.address, "Master.Shutdown", new(struct{}), &reply) 62 | if ok == false { 63 | fmt.Printf("Cleanup: RPC %s error\n", mr.address) 64 | } 65 | debug("cleanupRegistration: done\n") 66 | } 67 | -------------------------------------------------------------------------------- /6.824-2016/src/mapreduce/master_splitmerge.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import ( 4 | "bufio" 5 | "encoding/json" 6 | "fmt" 7 | "log" 8 | "os" 9 | "sort" 10 | ) 11 | 12 | // merge combines the results of the many reduce jobs into a single output file 13 | // XXX use merge sort 14 | func (mr *Master) merge() { 15 | debug("Merge phase") 16 | kvs := make(map[string]string) 17 | for i := 0; i < mr.nReduce; i++ { 18 | p := mergeName(mr.jobName, i) 19 | fmt.Printf("Merge: read %s\n", p) 20 | file, err := os.Open(p) 21 | if err != nil { 22 | log.Fatal("Merge: ", err) 23 | } 24 | dec := json.NewDecoder(file) 25 | for { 26 | var kv KeyValue 27 | err = dec.Decode(&kv) 28 | if err != nil { 29 | break 30 | } 31 | kvs[kv.Key] = kv.Value 32 | } 33 | file.Close() 34 | } 35 | var keys []string 36 | for k := range kvs { 37 | keys = append(keys, k) 38 | } 39 | sort.Strings(keys) 40 | 41 | file, err := os.Create("mrtmp." + mr.jobName) 42 | if err != nil { 43 | log.Fatal("Merge: create ", err) 44 | } 45 | w := bufio.NewWriter(file) 46 | for _, k := range keys { 47 | fmt.Fprintf(w, "%s: %s\n", k, kvs[k]) 48 | } 49 | w.Flush() 50 | file.Close() 51 | } 52 | 53 | // removeFile is a simple wrapper around os.Remove that logs errors. 54 | func removeFile(n string) { 55 | err := os.Remove(n) 56 | if err != nil { 57 | log.Fatal("CleanupFiles ", err) 58 | } 59 | } 60 | 61 | // CleanupFiles removes all intermediate files produced by running mapreduce. 62 | func (mr *Master) CleanupFiles() { 63 | for i := range mr.files { 64 | for j := 0; j < mr.nReduce; j++ { 65 | removeFile(reduceName(mr.jobName, i, j)) 66 | } 67 | } 68 | for i := 0; i < mr.nReduce; i++ { 69 | removeFile(mergeName(mr.jobName, i)) 70 | } 71 | removeFile("mrtmp." + mr.jobName) 72 | } 73 | -------------------------------------------------------------------------------- /6.824-2016/src/mapreduce/readme.go: -------------------------------------------------------------------------------- 1 | // Package mapreduce provides a simple mapreduce library with a sequential 2 | // implementation. Applications should normally call Distributed() [located in 3 | // master.go] to start a job, but may instead call Sequential() [also in 4 | // master.go] to get a sequential execution for debugging purposes. 5 | // 6 | // The flow of the mapreduce implementation is as follows: 7 | // 8 | // 1. The application provides a number of input files, a map function, a 9 | // reduce function, and the number of reduce tasks (nReduce). 10 | // 2. A master is created with this knowledge. It spins up an RPC server (see 11 | // master_rpc.go), and waits for workers to register (using the RPC call 12 | // Register() [defined in master.go]). As tasks become available (in steps 13 | // 4 and 5), schedule() [schedule.go] decides how to assign those tasks to 14 | // workers, and how to handle worker failures. 15 | // 3. The master considers each input file one map tasks, and makes a call to 16 | // doMap() [common_map.go] at least once for each task. It does so either 17 | // directly (when using Sequential()) or by issuing the DoJob RPC on a 18 | // worker [worker.go]. Each call to doMap() reads the appropriate file, 19 | // calls the map function on that file's contents, and produces nReduce 20 | // files for each map file. Thus, there will be #files x nReduce files 21 | // after all map tasks are done: 22 | // 23 | // f0-0, ..., f0-0, f0-, ..., 24 | // f<#files-1>-0, ... f<#files-1>-. 25 | // 26 | // 4. The master next makes a call to doReduce() [common_reduce.go] at least 27 | // once for each reduce task. As for doMap(), it does so either directly or 28 | // through a worker. doReduce() collects nReduce reduce files from each 29 | // map (f-*-), and runs the reduce function on those files. This 30 | // produces nReduce result files. 31 | // 5. The master calls mr.merge() [master_splitmerge.go], which merges all 32 | // the nReduce files produced by the previous step into a single output. 33 | // 6. The master sends a Shutdown RPC to each of its workers, and then shuts 34 | // down its own RPC server. 35 | // 36 | // TODO: 37 | // You will have to write/modify doMap, doReduce, and schedule yourself. These 38 | // are located in common_map.go, common_reduce.go, and schedule.go 39 | // respectively. You will also have to write the map and reduce functions in 40 | // ../main/wc.go. 41 | // 42 | // You should not need to modify any other files, but reading them might be 43 | // useful in order to understand how the other methods fit into the overall 44 | // architecture of the system. 45 | package mapreduce 46 | -------------------------------------------------------------------------------- /6.824-2016/src/mapreduce/schedule.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | ) 7 | 8 | // schedule starts and waits for all tasks in the given phase (Map or Reduce). 9 | func (mr *Master) schedule(phase jobPhase) { 10 | var ntasks int 11 | var nios int // number of inputs (for reduce) or outputs (for map) 12 | switch phase { 13 | case mapPhase: 14 | ntasks = len(mr.files) 15 | nios = mr.nReduce 16 | case reducePhase: 17 | ntasks = mr.nReduce 18 | nios = len(mr.files) 19 | } 20 | 21 | fmt.Printf("Schedule: %v %v tasks (%d I/Os)\n", ntasks, phase, nios) 22 | 23 | // All ntasks tasks have to be scheduled on workers, and only once all of 24 | // them have been completed successfully should the function return. 25 | // Remember that workers may fail, and that any given worker may finish 26 | // multiple tasks. 27 | // 28 | // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO 29 | // 30 | 31 | var wg sync.WaitGroup 32 | 33 | for i := 0; i < ntasks; i++ { 34 | wg.Add(1) 35 | 36 | var args DoTaskArgs 37 | args.File = mr.files[i] 38 | args.JobName = mr.jobName 39 | args.NumOtherPhase = nios 40 | args.Phase = phase 41 | args.TaskNumber = i 42 | 43 | go func() { 44 | var worker string 45 | for { 46 | worker = <-mr.registerChannel 47 | ok := call(worker, "Worker.DoTask", args, nil) 48 | if ok == true { 49 | break 50 | } 51 | } 52 | wg.Done() 53 | mr.registerChannel <- worker 54 | }() 55 | } 56 | 57 | wg.Wait() 58 | 59 | fmt.Printf("Schedule: %v phase done\n", phase) 60 | } 61 | -------------------------------------------------------------------------------- /6.824-2016/src/mapreduce/test_test.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "bufio" 9 | "log" 10 | "os" 11 | "sort" 12 | "strconv" 13 | "strings" 14 | ) 15 | 16 | const ( 17 | nNumber = 100000 18 | nMap = 100 19 | nReduce = 50 20 | ) 21 | 22 | // Create input file with N numbers 23 | // Check if we have N numbers in output file 24 | 25 | // Split in words 26 | func MapFunc(file string, value string) (res []KeyValue) { 27 | debug("Map %v\n", value) 28 | words := strings.Fields(value) 29 | for _, w := range words { 30 | kv := KeyValue{w, ""} 31 | res = append(res, kv) 32 | } 33 | return 34 | } 35 | 36 | // Just return key 37 | func ReduceFunc(key string, values []string) string { 38 | for _, e := range values { 39 | debug("Reduce %s %v\n", key, e) 40 | } 41 | return "" 42 | } 43 | 44 | // Checks input file agaist output file: each input number should show up 45 | // in the output file in string sorted order 46 | func check(t *testing.T, files []string) { 47 | output, err := os.Open("mrtmp.test") 48 | if err != nil { 49 | log.Fatal("check: ", err) 50 | } 51 | defer output.Close() 52 | 53 | var lines []string 54 | for _, f := range files { 55 | input, err := os.Open(f) 56 | if err != nil { 57 | log.Fatal("check: ", err) 58 | } 59 | defer input.Close() 60 | inputScanner := bufio.NewScanner(input) 61 | for inputScanner.Scan() { 62 | lines = append(lines, inputScanner.Text()) 63 | } 64 | } 65 | 66 | sort.Strings(lines) 67 | 68 | outputScanner := bufio.NewScanner(output) 69 | i := 0 70 | for outputScanner.Scan() { 71 | var v1 int 72 | var v2 int 73 | text := outputScanner.Text() 74 | n, err := fmt.Sscanf(lines[i], "%d", &v1) 75 | if n == 1 && err == nil { 76 | n, err = fmt.Sscanf(text, "%d", &v2) 77 | } 78 | if err != nil || v1 != v2 { 79 | t.Fatalf("line %d: %d != %d err %v\n", i, v1, v2, err) 80 | } 81 | i++ 82 | } 83 | if i != nNumber { 84 | t.Fatalf("Expected %d lines in output\n", nNumber) 85 | } 86 | } 87 | 88 | // Workers report back how many RPCs they have processed in the Shutdown reply. 89 | // Check that they processed at least 1 RPC. 90 | func checkWorker(t *testing.T, l []int) { 91 | for _, tasks := range l { 92 | if tasks == 0 { 93 | t.Fatalf("Some worker didn't do any work\n") 94 | } 95 | } 96 | } 97 | 98 | // Make input file 99 | func makeInputs(num int) []string { 100 | var names []string 101 | var i = 0 102 | for f := 0; f < num; f++ { 103 | names = append(names, fmt.Sprintf("824-mrinput-%d.txt", f)) 104 | file, err := os.Create(names[f]) 105 | if err != nil { 106 | log.Fatal("mkInput: ", err) 107 | } 108 | w := bufio.NewWriter(file) 109 | for i < (f+1)*(nNumber/num) { 110 | fmt.Fprintf(w, "%d\n", i) 111 | i++ 112 | } 113 | w.Flush() 114 | file.Close() 115 | } 116 | return names 117 | } 118 | 119 | // Cook up a unique-ish UNIX-domain socket name 120 | // in /var/tmp. can't use current directory since 121 | // AFS doesn't support UNIX-domain sockets. 122 | func port(suffix string) string { 123 | s := "/var/tmp/824-" 124 | s += strconv.Itoa(os.Getuid()) + "/" 125 | os.Mkdir(s, 0777) 126 | s += "mr" 127 | s += strconv.Itoa(os.Getpid()) + "-" 128 | s += suffix 129 | return s 130 | } 131 | 132 | func setup() *Master { 133 | files := makeInputs(nMap) 134 | master := port("master") 135 | mr := Distributed("test", files, nReduce, master) 136 | return mr 137 | } 138 | 139 | func cleanup(mr *Master) { 140 | mr.CleanupFiles() 141 | for _, f := range mr.files { 142 | removeFile(f) 143 | } 144 | } 145 | 146 | func TestSequentialSingle(t *testing.T) { 147 | mr := Sequential("test", makeInputs(1), 1, MapFunc, ReduceFunc) 148 | mr.Wait() 149 | check(t, mr.files) 150 | checkWorker(t, mr.stats) 151 | cleanup(mr) 152 | } 153 | 154 | func TestSequentialMany(t *testing.T) { 155 | mr := Sequential("test", makeInputs(5), 3, MapFunc, ReduceFunc) 156 | mr.Wait() 157 | check(t, mr.files) 158 | checkWorker(t, mr.stats) 159 | cleanup(mr) 160 | } 161 | 162 | func TestBasic(t *testing.T) { 163 | mr := setup() 164 | for i := 0; i < 2; i++ { 165 | go RunWorker(mr.address, port("worker"+strconv.Itoa(i)), 166 | MapFunc, ReduceFunc, -1) 167 | } 168 | mr.Wait() 169 | check(t, mr.files) 170 | checkWorker(t, mr.stats) 171 | cleanup(mr) 172 | } 173 | 174 | func TestOneFailure(t *testing.T) { 175 | mr := setup() 176 | // Start 2 workers that fail after 10 tasks 177 | go RunWorker(mr.address, port("worker"+strconv.Itoa(0)), 178 | MapFunc, ReduceFunc, 10) 179 | go RunWorker(mr.address, port("worker"+strconv.Itoa(1)), 180 | MapFunc, ReduceFunc, -1) 181 | mr.Wait() 182 | check(t, mr.files) 183 | checkWorker(t, mr.stats) 184 | cleanup(mr) 185 | } 186 | 187 | func TestManyFailures(t *testing.T) { 188 | mr := setup() 189 | i := 0 190 | done := false 191 | for !done { 192 | select { 193 | case done = <-mr.doneChannel: 194 | check(t, mr.files) 195 | cleanup(mr) 196 | break 197 | default: 198 | // Start 2 workers each sec. The workers fail after 10 tasks 199 | w := port("worker" + strconv.Itoa(i)) 200 | go RunWorker(mr.address, w, MapFunc, ReduceFunc, 10) 201 | i++ 202 | w = port("worker" + strconv.Itoa(i)) 203 | go RunWorker(mr.address, w, MapFunc, ReduceFunc, 10) 204 | i++ 205 | time.Sleep(1 * time.Second) 206 | } 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /6.824-2016/src/mapreduce/worker.go: -------------------------------------------------------------------------------- 1 | package mapreduce 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net" 7 | "net/rpc" 8 | "os" 9 | "sync" 10 | ) 11 | 12 | // Worker holds the state for a server waiting for DoTask or Shutdown RPCs 13 | type Worker struct { 14 | sync.Mutex 15 | 16 | name string 17 | Map func(string, string) []KeyValue 18 | Reduce func(string, []string) string 19 | nRPC int // protected by mutex 20 | nTasks int // protected by mutex 21 | l net.Listener 22 | } 23 | 24 | // DoTask is called by the master when a new task is being scheduled on this 25 | // worker. 26 | func (wk *Worker) DoTask(arg *DoTaskArgs, _ *struct{}) error { 27 | fmt.Printf("%s: given %v task #%d on file %s (nios: %d)\n", 28 | wk.name, arg.Phase, arg.TaskNumber, arg.File, arg.NumOtherPhase) 29 | 30 | switch arg.Phase { 31 | case mapPhase: 32 | doMap(arg.JobName, arg.TaskNumber, arg.File, arg.NumOtherPhase, wk.Map) 33 | case reducePhase: 34 | doReduce(arg.JobName, arg.TaskNumber, arg.NumOtherPhase, wk.Reduce) 35 | } 36 | 37 | fmt.Printf("%s: %v task #%d done\n", wk.name, arg.Phase, arg.TaskNumber) 38 | return nil 39 | } 40 | 41 | // Shutdown is called by the master when all work has been completed. 42 | // We should respond with the number of tasks we have processed. 43 | func (wk *Worker) Shutdown(_ *struct{}, res *ShutdownReply) error { 44 | debug("Shutdown %s\n", wk.name) 45 | wk.Lock() 46 | defer wk.Unlock() 47 | res.Ntasks = wk.nTasks 48 | wk.nRPC = 1 49 | wk.nTasks-- // Don't count the shutdown RPC 50 | return nil 51 | } 52 | 53 | // Tell the master we exist and ready to work 54 | func (wk *Worker) register(master string) { 55 | args := new(RegisterArgs) 56 | args.Worker = wk.name 57 | ok := call(master, "Master.Register", args, new(struct{})) 58 | if ok == false { 59 | fmt.Printf("Register: RPC %s register error\n", master) 60 | } 61 | } 62 | 63 | // RunWorker sets up a connection with the master, registers its address, and 64 | // waits for tasks to be scheduled. 65 | func RunWorker(MasterAddress string, me string, 66 | MapFunc func(string, string) []KeyValue, 67 | ReduceFunc func(string, []string) string, 68 | nRPC int, 69 | ) { 70 | debug("RunWorker %s\n", me) 71 | wk := new(Worker) 72 | wk.name = me 73 | wk.Map = MapFunc 74 | wk.Reduce = ReduceFunc 75 | wk.nRPC = nRPC 76 | rpcs := rpc.NewServer() 77 | rpcs.Register(wk) 78 | os.Remove(me) // only needed for "unix" 79 | l, e := net.Listen("unix", me) 80 | if e != nil { 81 | log.Fatal("RunWorker: worker ", me, " error: ", e) 82 | } 83 | wk.l = l 84 | wk.register(MasterAddress) 85 | 86 | // DON'T MODIFY CODE BELOW 87 | for { 88 | wk.Lock() 89 | if wk.nRPC == 0 { 90 | wk.Unlock() 91 | break 92 | } 93 | wk.Unlock() 94 | conn, err := wk.l.Accept() 95 | if err == nil { 96 | wk.Lock() 97 | wk.nRPC-- 98 | wk.Unlock() 99 | go rpcs.ServeConn(conn) 100 | wk.Lock() 101 | wk.nTasks++ 102 | wk.Unlock() 103 | } else { 104 | break 105 | } 106 | } 107 | wk.l.Close() 108 | debug("RunWorker %s exit\n", me) 109 | } 110 | -------------------------------------------------------------------------------- /6.824-2016/src/paxos-shardkv/client.go: -------------------------------------------------------------------------------- 1 | package shardkv 2 | 3 | import "shardmaster" 4 | import "net/rpc" 5 | import "time" 6 | import "sync" 7 | import "fmt" 8 | import "crypto/rand" 9 | import "math/big" 10 | 11 | type Clerk struct { 12 | mu sync.Mutex // one RPC at a time 13 | sm *shardmaster.Clerk 14 | config shardmaster.Config 15 | // You'll have to modify Clerk. 16 | } 17 | 18 | func nrand() int64 { 19 | max := big.NewInt(int64(1) << 62) 20 | bigx, _ := rand.Int(rand.Reader, max) 21 | x := bigx.Int64() 22 | return x 23 | } 24 | 25 | func MakeClerk(shardmasters []string) *Clerk { 26 | ck := new(Clerk) 27 | ck.sm = shardmaster.MakeClerk(shardmasters) 28 | // You'll have to modify MakeClerk. 29 | return ck 30 | } 31 | 32 | // 33 | // call() sends an RPC to the rpcname handler on server srv 34 | // with arguments args, waits for the reply, and leaves the 35 | // reply in reply. the reply argument should be a pointer 36 | // to a reply structure. 37 | // 38 | // the return value is true if the server responded, and false 39 | // if call() was not able to contact the server. in particular, 40 | // the reply's contents are only valid if call() returned true. 41 | // 42 | // you should assume that call() will return an 43 | // error after a while if the server is dead. 44 | // don't provide your own time-out mechanism. 45 | // 46 | // please use call() to send all RPCs, in client.go and server.go. 47 | // please don't change this function. 48 | // 49 | func call(srv string, rpcname string, 50 | args interface{}, reply interface{}) bool { 51 | c, errx := rpc.Dial("unix", srv) 52 | if errx != nil { 53 | return false 54 | } 55 | defer c.Close() 56 | 57 | err := c.Call(rpcname, args, reply) 58 | if err == nil { 59 | return true 60 | } 61 | 62 | fmt.Println(err) 63 | return false 64 | } 65 | 66 | // 67 | // which shard is a key in? 68 | // please use this function, 69 | // and please do not change it. 70 | // 71 | func key2shard(key string) int { 72 | shard := 0 73 | if len(key) > 0 { 74 | shard = int(key[0]) 75 | } 76 | shard %= shardmaster.NShards 77 | return shard 78 | } 79 | 80 | // 81 | // fetch the current value for a key. 82 | // returns "" if the key does not exist. 83 | // keeps trying forever in the face of all other errors. 84 | // 85 | func (ck *Clerk) Get(key string) string { 86 | ck.mu.Lock() 87 | defer ck.mu.Unlock() 88 | 89 | // You'll have to modify Get(). 90 | 91 | for { 92 | shard := key2shard(key) 93 | 94 | gid := ck.config.Shards[shard] 95 | 96 | servers, ok := ck.config.Groups[gid] 97 | 98 | if ok { 99 | // try each server in the shard's replication group. 100 | for _, srv := range servers { 101 | args := &GetArgs{} 102 | args.Key = key 103 | var reply GetReply 104 | ok := call(srv, "ShardKV.Get", args, &reply) 105 | if ok && (reply.Err == OK || reply.Err == ErrNoKey) { 106 | return reply.Value 107 | } 108 | if ok && (reply.Err == ErrWrongGroup) { 109 | break 110 | } 111 | } 112 | } 113 | 114 | time.Sleep(100 * time.Millisecond) 115 | 116 | // ask master for a new configuration. 117 | ck.config = ck.sm.Query(-1) 118 | } 119 | } 120 | 121 | // send a Put or Append request. 122 | func (ck *Clerk) PutAppend(key string, value string, op string) { 123 | ck.mu.Lock() 124 | defer ck.mu.Unlock() 125 | 126 | // You'll have to modify PutAppend(). 127 | 128 | for { 129 | shard := key2shard(key) 130 | 131 | gid := ck.config.Shards[shard] 132 | 133 | servers, ok := ck.config.Groups[gid] 134 | 135 | if ok { 136 | // try each server in the shard's replication group. 137 | for _, srv := range servers { 138 | args := &PutAppendArgs{} 139 | args.Key = key 140 | args.Value = value 141 | args.Op = op 142 | var reply PutAppendReply 143 | ok := call(srv, "ShardKV.PutAppend", args, &reply) 144 | if ok && reply.Err == OK { 145 | return 146 | } 147 | if ok && (reply.Err == ErrWrongGroup) { 148 | break 149 | } 150 | } 151 | } 152 | 153 | time.Sleep(100 * time.Millisecond) 154 | 155 | // ask master for a new configuration. 156 | ck.config = ck.sm.Query(-1) 157 | } 158 | } 159 | 160 | func (ck *Clerk) Put(key string, value string) { 161 | ck.PutAppend(key, value, "Put") 162 | } 163 | func (ck *Clerk) Append(key string, value string) { 164 | ck.PutAppend(key, value, "Append") 165 | } 166 | -------------------------------------------------------------------------------- /6.824-2016/src/paxos-shardkv/common.go: -------------------------------------------------------------------------------- 1 | package shardkv 2 | 3 | // 4 | // Sharded key/value server. 5 | // Lots of replica groups, each running op-at-a-time paxos. 6 | // Shardmaster decides which group serves each shard. 7 | // Shardmaster may change shard assignment from time to time. 8 | // 9 | // You will have to modify these definitions. 10 | // 11 | 12 | const ( 13 | OK = "OK" 14 | ErrNoKey = "ErrNoKey" 15 | ErrWrongGroup = "ErrWrongGroup" 16 | ) 17 | 18 | type Err string 19 | 20 | type PutAppendArgs struct { 21 | Key string 22 | Value string 23 | Op string // "Put" or "Append" 24 | // You'll have to add definitions here. 25 | // Field names must start with capital letters, 26 | // otherwise RPC will break. 27 | 28 | } 29 | 30 | type PutAppendReply struct { 31 | Err Err 32 | } 33 | 34 | type GetArgs struct { 35 | Key string 36 | // You'll have to add definitions here. 37 | } 38 | 39 | type GetReply struct { 40 | Err Err 41 | Value string 42 | } 43 | -------------------------------------------------------------------------------- /6.824-2016/src/paxos-shardkv/server.go: -------------------------------------------------------------------------------- 1 | package shardkv 2 | 3 | import "net" 4 | import "fmt" 5 | import "net/rpc" 6 | import "log" 7 | import "time" 8 | import "paxos" 9 | import "sync" 10 | import "sync/atomic" 11 | import "os" 12 | import "syscall" 13 | import "encoding/gob" 14 | import "math/rand" 15 | import "shardmaster" 16 | 17 | const Debug = 0 18 | 19 | func DPrintf(format string, a ...interface{}) (n int, err error) { 20 | if Debug > 0 { 21 | log.Printf(format, a...) 22 | } 23 | return 24 | } 25 | 26 | type Op struct { 27 | // Your definitions here. 28 | } 29 | 30 | type ShardKV struct { 31 | mu sync.Mutex 32 | l net.Listener 33 | me int 34 | dead int32 // for testing 35 | unreliable int32 // for testing 36 | sm *shardmaster.Clerk 37 | px *paxos.Paxos 38 | 39 | gid int64 // my replica group ID 40 | 41 | // Your definitions here. 42 | } 43 | 44 | func (kv *ShardKV) Get(args *GetArgs, reply *GetReply) error { 45 | // Your code here. 46 | return nil 47 | } 48 | 49 | // RPC handler for client Put and Append requests 50 | func (kv *ShardKV) PutAppend(args *PutAppendArgs, reply *PutAppendReply) error { 51 | // Your code here. 52 | return nil 53 | } 54 | 55 | // 56 | // Ask the shardmaster if there's a new configuration; 57 | // if so, re-configure. 58 | // 59 | func (kv *ShardKV) tick() { 60 | } 61 | 62 | // tell the server to shut itself down. 63 | // please don't change these two functions. 64 | func (kv *ShardKV) kill() { 65 | atomic.StoreInt32(&kv.dead, 1) 66 | kv.l.Close() 67 | kv.px.Kill() 68 | } 69 | 70 | // call this to find out if the server is dead. 71 | func (kv *ShardKV) isdead() bool { 72 | return atomic.LoadInt32(&kv.dead) != 0 73 | } 74 | 75 | // please do not change these two functions. 76 | func (kv *ShardKV) Setunreliable(what bool) { 77 | if what { 78 | atomic.StoreInt32(&kv.unreliable, 1) 79 | } else { 80 | atomic.StoreInt32(&kv.unreliable, 0) 81 | } 82 | } 83 | 84 | func (kv *ShardKV) isunreliable() bool { 85 | return atomic.LoadInt32(&kv.unreliable) != 0 86 | } 87 | 88 | // 89 | // Start a shardkv server. 90 | // gid is the ID of the server's replica group. 91 | // shardmasters[] contains the ports of the 92 | // servers that implement the shardmaster. 93 | // servers[] contains the ports of the servers 94 | // in this replica group. 95 | // Me is the index of this server in servers[]. 96 | // 97 | func StartServer(gid int64, shardmasters []string, 98 | servers []string, me int) *ShardKV { 99 | gob.Register(Op{}) 100 | 101 | kv := new(ShardKV) 102 | kv.me = me 103 | kv.gid = gid 104 | kv.sm = shardmaster.MakeClerk(shardmasters) 105 | 106 | // Your initialization code here. 107 | // Don't call Join(). 108 | 109 | rpcs := rpc.NewServer() 110 | rpcs.Register(kv) 111 | 112 | kv.px = paxos.Make(servers, me, rpcs) 113 | 114 | os.Remove(servers[me]) 115 | l, e := net.Listen("unix", servers[me]) 116 | if e != nil { 117 | log.Fatal("listen error: ", e) 118 | } 119 | kv.l = l 120 | 121 | // please do not change any of the following code, 122 | // or do anything to subvert it. 123 | 124 | go func() { 125 | for kv.isdead() == false { 126 | conn, err := kv.l.Accept() 127 | if err == nil && kv.isdead() == false { 128 | if kv.isunreliable() && (rand.Int63()%1000) < 100 { 129 | // discard the request. 130 | conn.Close() 131 | } else if kv.isunreliable() && (rand.Int63()%1000) < 200 { 132 | // process the request but force discard of reply. 133 | c1 := conn.(*net.UnixConn) 134 | f, _ := c1.File() 135 | err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR) 136 | if err != nil { 137 | fmt.Printf("shutdown: %v\n", err) 138 | } 139 | go rpcs.ServeConn(conn) 140 | } else { 141 | go rpcs.ServeConn(conn) 142 | } 143 | } else if err == nil { 144 | conn.Close() 145 | } 146 | if err != nil && kv.isdead() == false { 147 | fmt.Printf("ShardKV(%v) accept: %v\n", me, err.Error()) 148 | kv.kill() 149 | } 150 | } 151 | }() 152 | 153 | go func() { 154 | for kv.isdead() == false { 155 | kv.tick() 156 | time.Sleep(250 * time.Millisecond) 157 | } 158 | }() 159 | 160 | return kv 161 | } 162 | -------------------------------------------------------------------------------- /6.824-2016/src/paxos-shardmaster/client.go: -------------------------------------------------------------------------------- 1 | package shardmaster 2 | 3 | // 4 | // Shardmaster clerk. 5 | // Please don't change this file. 6 | // 7 | 8 | import "net/rpc" 9 | import "time" 10 | import "fmt" 11 | 12 | type Clerk struct { 13 | servers []string // shardmaster replicas 14 | } 15 | 16 | func MakeClerk(servers []string) *Clerk { 17 | ck := new(Clerk) 18 | ck.servers = servers 19 | return ck 20 | } 21 | 22 | // 23 | // call() sends an RPC to the rpcname handler on server srv 24 | // with arguments args, waits for the reply, and leaves the 25 | // reply in reply. the reply argument should be a pointer 26 | // to a reply structure. 27 | // 28 | // the return value is true if the server responded, and false 29 | // if call() was not able to contact the server. in particular, 30 | // the reply's contents are only valid if call() returned true. 31 | // 32 | // you should assume that call() will return an 33 | // error after a while if the server is dead. 34 | // don't provide your own time-out mechanism. 35 | // 36 | // please use call() to send all RPCs, in client.go and server.go. 37 | // please don't change this function. 38 | // 39 | func call(srv string, rpcname string, 40 | args interface{}, reply interface{}) bool { 41 | c, errx := rpc.Dial("unix", srv) 42 | if errx != nil { 43 | return false 44 | } 45 | defer c.Close() 46 | 47 | err := c.Call(rpcname, args, reply) 48 | if err == nil { 49 | return true 50 | } 51 | 52 | fmt.Println(err) 53 | return false 54 | } 55 | 56 | func (ck *Clerk) Query(num int) Config { 57 | for { 58 | // try each known server. 59 | for _, srv := range ck.servers { 60 | args := &QueryArgs{} 61 | args.Num = num 62 | var reply QueryReply 63 | ok := call(srv, "ShardMaster.Query", args, &reply) 64 | if ok { 65 | return reply.Config 66 | } 67 | } 68 | time.Sleep(100 * time.Millisecond) 69 | } 70 | } 71 | 72 | func (ck *Clerk) Join(gid int64, servers []string) { 73 | for { 74 | // try each known server. 75 | for _, srv := range ck.servers { 76 | args := &JoinArgs{} 77 | args.GID = gid 78 | args.Servers = servers 79 | var reply JoinReply 80 | ok := call(srv, "ShardMaster.Join", args, &reply) 81 | if ok { 82 | return 83 | } 84 | } 85 | time.Sleep(100 * time.Millisecond) 86 | } 87 | } 88 | 89 | func (ck *Clerk) Leave(gid int64) { 90 | for { 91 | // try each known server. 92 | for _, srv := range ck.servers { 93 | args := &LeaveArgs{} 94 | args.GID = gid 95 | var reply LeaveReply 96 | ok := call(srv, "ShardMaster.Leave", args, &reply) 97 | if ok { 98 | return 99 | } 100 | } 101 | time.Sleep(100 * time.Millisecond) 102 | } 103 | } 104 | 105 | func (ck *Clerk) Move(shard int, gid int64) { 106 | for { 107 | // try each known server. 108 | for _, srv := range ck.servers { 109 | args := &MoveArgs{} 110 | args.Shard = shard 111 | args.GID = gid 112 | var reply MoveReply 113 | ok := call(srv, "ShardMaster.Move", args, &reply) 114 | if ok { 115 | return 116 | } 117 | } 118 | time.Sleep(100 * time.Millisecond) 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /6.824-2016/src/paxos-shardmaster/common.go: -------------------------------------------------------------------------------- 1 | package shardmaster 2 | 3 | // 4 | // Master shard server: assigns shards to replication groups. 5 | // 6 | // RPC interface: 7 | // Join(gid, servers) -- replica group gid is joining, give it some shards. 8 | // Leave(gid) -- replica group gid is retiring, hand off all its shards. 9 | // Move(shard, gid) -- hand off one shard from current owner to gid. 10 | // Query(num) -> fetch Config # num, or latest config if num==-1. 11 | // 12 | // A Config (configuration) describes a set of replica groups, and the 13 | // replica group responsible for each shard. Configs are numbered. Config 14 | // #0 is the initial configuration, with no groups and all shards 15 | // assigned to group 0 (the invalid group). 16 | // 17 | // A GID is a replica group ID. GIDs must be uniqe and > 0. 18 | // Once a GID joins, and leaves, it should never join again. 19 | // 20 | // Please don't change this file. 21 | // 22 | 23 | const NShards = 10 24 | 25 | type Config struct { 26 | Num int // config number 27 | Shards [NShards]int64 // shard -> gid 28 | Groups map[int64][]string // gid -> servers[] 29 | } 30 | 31 | type JoinArgs struct { 32 | GID int64 // unique replica group ID 33 | Servers []string // group server ports 34 | } 35 | 36 | type JoinReply struct { 37 | } 38 | 39 | type LeaveArgs struct { 40 | GID int64 41 | } 42 | 43 | type LeaveReply struct { 44 | } 45 | 46 | type MoveArgs struct { 47 | Shard int 48 | GID int64 49 | } 50 | 51 | type MoveReply struct { 52 | } 53 | 54 | type QueryArgs struct { 55 | Num int // desired config number 56 | } 57 | 58 | type QueryReply struct { 59 | Config Config 60 | } 61 | -------------------------------------------------------------------------------- /6.824-2016/src/paxos-shardmaster/server.go: -------------------------------------------------------------------------------- 1 | package shardmaster 2 | 3 | import "net" 4 | import "fmt" 5 | import "net/rpc" 6 | import "log" 7 | 8 | import "paxos" 9 | import "sync" 10 | import "sync/atomic" 11 | import "os" 12 | import "syscall" 13 | import "encoding/gob" 14 | import "math/rand" 15 | 16 | type ShardMaster struct { 17 | mu sync.Mutex 18 | l net.Listener 19 | me int 20 | dead int32 // for testing 21 | unreliable int32 // for testing 22 | px *paxos.Paxos 23 | 24 | configs []Config // indexed by config num 25 | } 26 | 27 | type Op struct { 28 | // Your data here. 29 | } 30 | 31 | func (sm *ShardMaster) Join(args *JoinArgs, reply *JoinReply) error { 32 | // Your code here. 33 | 34 | return nil 35 | } 36 | 37 | func (sm *ShardMaster) Leave(args *LeaveArgs, reply *LeaveReply) error { 38 | // Your code here. 39 | 40 | return nil 41 | } 42 | 43 | func (sm *ShardMaster) Move(args *MoveArgs, reply *MoveReply) error { 44 | // Your code here. 45 | 46 | return nil 47 | } 48 | 49 | func (sm *ShardMaster) Query(args *QueryArgs, reply *QueryReply) error { 50 | // Your code here. 51 | 52 | return nil 53 | } 54 | 55 | // please don't change these two functions. 56 | func (sm *ShardMaster) Kill() { 57 | atomic.StoreInt32(&sm.dead, 1) 58 | sm.l.Close() 59 | sm.px.Kill() 60 | } 61 | 62 | // call this to find out if the server is dead. 63 | func (sm *ShardMaster) isdead() bool { 64 | return atomic.LoadInt32(&sm.dead) != 0 65 | } 66 | 67 | // please do not change these two functions. 68 | func (sm *ShardMaster) setunreliable(what bool) { 69 | if what { 70 | atomic.StoreInt32(&sm.unreliable, 1) 71 | } else { 72 | atomic.StoreInt32(&sm.unreliable, 0) 73 | } 74 | } 75 | 76 | func (sm *ShardMaster) isunreliable() bool { 77 | return atomic.LoadInt32(&sm.unreliable) != 0 78 | } 79 | 80 | // 81 | // servers[] contains the ports of the set of 82 | // servers that will cooperate via Paxos to 83 | // form the fault-tolerant shardmaster service. 84 | // me is the index of the current server in servers[]. 85 | // 86 | func StartServer(servers []string, me int) *ShardMaster { 87 | sm := new(ShardMaster) 88 | sm.me = me 89 | 90 | sm.configs = make([]Config, 1) 91 | sm.configs[0].Groups = map[int64][]string{} 92 | 93 | rpcs := rpc.NewServer() 94 | 95 | gob.Register(Op{}) 96 | rpcs.Register(sm) 97 | sm.px = paxos.Make(servers, me, rpcs) 98 | 99 | os.Remove(servers[me]) 100 | l, e := net.Listen("unix", servers[me]) 101 | if e != nil { 102 | log.Fatal("listen error: ", e) 103 | } 104 | sm.l = l 105 | 106 | // please do not change any of the following code, 107 | // or do anything to subvert it. 108 | 109 | go func() { 110 | for sm.isdead() == false { 111 | conn, err := sm.l.Accept() 112 | if err == nil && sm.isdead() == false { 113 | if sm.isunreliable() && (rand.Int63()%1000) < 100 { 114 | // discard the request. 115 | conn.Close() 116 | } else if sm.isunreliable() && (rand.Int63()%1000) < 200 { 117 | // process the request but force discard of reply. 118 | c1 := conn.(*net.UnixConn) 119 | f, _ := c1.File() 120 | err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR) 121 | if err != nil { 122 | fmt.Printf("shutdown: %v\n", err) 123 | } 124 | go rpcs.ServeConn(conn) 125 | } else { 126 | go rpcs.ServeConn(conn) 127 | } 128 | } else if err == nil { 129 | conn.Close() 130 | } 131 | if err != nil && sm.isdead() == false { 132 | fmt.Printf("ShardMaster(%v) accept: %v\n", me, err.Error()) 133 | sm.Kill() 134 | } 135 | } 136 | }() 137 | 138 | return sm 139 | } 140 | -------------------------------------------------------------------------------- /6.824-2016/src/pbservice/client.go: -------------------------------------------------------------------------------- 1 | package pbservice 2 | 3 | import "viewservice" 4 | import "net/rpc" 5 | import "fmt" 6 | 7 | import "crypto/rand" 8 | import "math/big" 9 | 10 | type Clerk struct { 11 | vs *viewservice.Clerk 12 | // Your declarations here 13 | } 14 | 15 | // this may come in handy. 16 | func nrand() int64 { 17 | max := big.NewInt(int64(1) << 62) 18 | bigx, _ := rand.Int(rand.Reader, max) 19 | x := bigx.Int64() 20 | return x 21 | } 22 | 23 | func MakeClerk(vshost string, me string) *Clerk { 24 | ck := new(Clerk) 25 | ck.vs = viewservice.MakeClerk(me, vshost) 26 | // Your ck.* initializations here 27 | 28 | return ck 29 | } 30 | 31 | // 32 | // call() sends an RPC to the rpcname handler on server srv 33 | // with arguments args, waits for the reply, and leaves the 34 | // reply in reply. the reply argument should be a pointer 35 | // to a reply structure. 36 | // 37 | // the return value is true if the server responded, and false 38 | // if call() was not able to contact the server. in particular, 39 | // the reply's contents are only valid if call() returned true. 40 | // 41 | // you should assume that call() will return an 42 | // error after a while if the server is dead. 43 | // don't provide your own time-out mechanism. 44 | // 45 | // please use call() to send all RPCs, in client.go and server.go. 46 | // please don't change this function. 47 | // 48 | func call(srv string, rpcname string, 49 | args interface{}, reply interface{}) bool { 50 | c, errx := rpc.Dial("unix", srv) 51 | if errx != nil { 52 | return false 53 | } 54 | defer c.Close() 55 | 56 | err := c.Call(rpcname, args, reply) 57 | if err == nil { 58 | return true 59 | } 60 | 61 | fmt.Println(err) 62 | return false 63 | } 64 | 65 | // 66 | // fetch a key's value from the current primary; 67 | // if they key has never been set, return "". 68 | // Get() must keep trying until it either the 69 | // primary replies with the value or the primary 70 | // says the key doesn't exist (has never been Put(). 71 | // 72 | func (ck *Clerk) Get(key string) string { 73 | 74 | // Your code here. 75 | 76 | return "???" 77 | } 78 | 79 | // 80 | // send a Put or Append RPC 81 | // 82 | func (ck *Clerk) PutAppend(key string, value string, op string) { 83 | 84 | // Your code here. 85 | } 86 | 87 | // 88 | // tell the primary to update key's value. 89 | // must keep trying until it succeeds. 90 | // 91 | func (ck *Clerk) Put(key string, value string) { 92 | ck.PutAppend(key, value, "Put") 93 | } 94 | 95 | // 96 | // tell the primary to append to key's value. 97 | // must keep trying until it succeeds. 98 | // 99 | func (ck *Clerk) Append(key string, value string) { 100 | ck.PutAppend(key, value, "Append") 101 | } 102 | -------------------------------------------------------------------------------- /6.824-2016/src/pbservice/common.go: -------------------------------------------------------------------------------- 1 | package pbservice 2 | 3 | const ( 4 | OK = "OK" 5 | ErrNoKey = "ErrNoKey" 6 | ErrWrongServer = "ErrWrongServer" 7 | ) 8 | 9 | type Err string 10 | 11 | // Put or Append 12 | type PutAppendArgs struct { 13 | Key string 14 | Value string 15 | // You'll have to add definitions here. 16 | 17 | // Field names must start with capital letters, 18 | // otherwise RPC will break. 19 | } 20 | 21 | type PutAppendReply struct { 22 | Err Err 23 | } 24 | 25 | type GetArgs struct { 26 | Key string 27 | // You'll have to add definitions here. 28 | } 29 | 30 | type GetReply struct { 31 | Err Err 32 | Value string 33 | } 34 | 35 | // Your RPC definitions here. 36 | -------------------------------------------------------------------------------- /6.824-2016/src/pbservice/server.go: -------------------------------------------------------------------------------- 1 | package pbservice 2 | 3 | import "net" 4 | import "fmt" 5 | import "net/rpc" 6 | import "log" 7 | import "time" 8 | import "viewservice" 9 | import "sync" 10 | import "sync/atomic" 11 | import "os" 12 | import "syscall" 13 | import "math/rand" 14 | 15 | type PBServer struct { 16 | mu sync.Mutex 17 | l net.Listener 18 | dead int32 // for testing 19 | unreliable int32 // for testing 20 | me string 21 | vs *viewservice.Clerk 22 | // Your declarations here. 23 | } 24 | 25 | func (pb *PBServer) Get(args *GetArgs, reply *GetReply) error { 26 | 27 | // Your code here. 28 | 29 | return nil 30 | } 31 | 32 | func (pb *PBServer) PutAppend(args *PutAppendArgs, reply *PutAppendReply) error { 33 | 34 | // Your code here. 35 | 36 | return nil 37 | } 38 | 39 | // 40 | // ping the viewserver periodically. 41 | // if view changed: 42 | // transition to new view. 43 | // manage transfer of state from primary to new backup. 44 | // 45 | func (pb *PBServer) tick() { 46 | 47 | // Your code here. 48 | } 49 | 50 | // tell the server to shut itself down. 51 | // please do not change these two functions. 52 | func (pb *PBServer) kill() { 53 | atomic.StoreInt32(&pb.dead, 1) 54 | pb.l.Close() 55 | } 56 | 57 | // call this to find out if the server is dead. 58 | func (pb *PBServer) isdead() bool { 59 | return atomic.LoadInt32(&pb.dead) != 0 60 | } 61 | 62 | // please do not change these two functions. 63 | func (pb *PBServer) setunreliable(what bool) { 64 | if what { 65 | atomic.StoreInt32(&pb.unreliable, 1) 66 | } else { 67 | atomic.StoreInt32(&pb.unreliable, 0) 68 | } 69 | } 70 | 71 | func (pb *PBServer) isunreliable() bool { 72 | return atomic.LoadInt32(&pb.unreliable) != 0 73 | } 74 | 75 | func StartServer(vshost string, me string) *PBServer { 76 | pb := new(PBServer) 77 | pb.me = me 78 | pb.vs = viewservice.MakeClerk(me, vshost) 79 | // Your pb.* initializations here. 80 | 81 | rpcs := rpc.NewServer() 82 | rpcs.Register(pb) 83 | 84 | os.Remove(pb.me) 85 | l, e := net.Listen("unix", pb.me) 86 | if e != nil { 87 | log.Fatal("listen error: ", e) 88 | } 89 | pb.l = l 90 | 91 | // please do not change any of the following code, 92 | // or do anything to subvert it. 93 | 94 | go func() { 95 | for pb.isdead() == false { 96 | conn, err := pb.l.Accept() 97 | if err == nil && pb.isdead() == false { 98 | if pb.isunreliable() && (rand.Int63()%1000) < 100 { 99 | // discard the request. 100 | conn.Close() 101 | } else if pb.isunreliable() && (rand.Int63()%1000) < 200 { 102 | // process the request but force discard of reply. 103 | c1 := conn.(*net.UnixConn) 104 | f, _ := c1.File() 105 | err := syscall.Shutdown(int(f.Fd()), syscall.SHUT_WR) 106 | if err != nil { 107 | fmt.Printf("shutdown: %v\n", err) 108 | } 109 | go rpcs.ServeConn(conn) 110 | } else { 111 | go rpcs.ServeConn(conn) 112 | } 113 | } else if err == nil { 114 | conn.Close() 115 | } 116 | if err != nil && pb.isdead() == false { 117 | fmt.Printf("PBServer(%v) accept: %v\n", me, err.Error()) 118 | pb.kill() 119 | } 120 | } 121 | }() 122 | 123 | go func() { 124 | for pb.isdead() == false { 125 | pb.tick() 126 | time.Sleep(viewservice.PingInterval) 127 | } 128 | }() 129 | 130 | return pb 131 | } 132 | -------------------------------------------------------------------------------- /6.824-2016/src/raft/persister.go: -------------------------------------------------------------------------------- 1 | package raft 2 | 3 | // 4 | // support for Raft and kvraft to save persistent 5 | // Raft state (log &c) and k/v server snapshots. 6 | // 7 | // we will use the original persister.go to test your code for grading. 8 | // so, while you can modify this code to help you debug, please 9 | // test with the original before submitting. 10 | // 11 | 12 | import "sync" 13 | 14 | type Persister struct { 15 | mu sync.Mutex 16 | raftstate []byte 17 | snapshot []byte 18 | } 19 | 20 | func MakePersister() *Persister { 21 | return &Persister{} 22 | } 23 | 24 | func (ps *Persister) Copy() *Persister { 25 | ps.mu.Lock() 26 | defer ps.mu.Unlock() 27 | np := MakePersister() 28 | np.raftstate = ps.raftstate 29 | np.snapshot = ps.snapshot 30 | return np 31 | } 32 | 33 | func (ps *Persister) SaveRaftState(data []byte) { 34 | ps.mu.Lock() 35 | defer ps.mu.Unlock() 36 | ps.raftstate = data 37 | } 38 | 39 | func (ps *Persister) ReadRaftState() []byte { 40 | ps.mu.Lock() 41 | defer ps.mu.Unlock() 42 | return ps.raftstate 43 | } 44 | 45 | func (ps *Persister) RaftStateSize() int { 46 | ps.mu.Lock() 47 | defer ps.mu.Unlock() 48 | return len(ps.raftstate) 49 | } 50 | 51 | func (ps *Persister) SaveSnapshot(snapshot []byte) { 52 | ps.mu.Lock() 53 | defer ps.mu.Unlock() 54 | ps.snapshot = snapshot 55 | } 56 | 57 | func (ps *Persister) ReadSnapshot() []byte { 58 | ps.mu.Lock() 59 | defer ps.mu.Unlock() 60 | return ps.snapshot 61 | } 62 | -------------------------------------------------------------------------------- /6.824-2016/src/raft/util.go: -------------------------------------------------------------------------------- 1 | package raft 2 | 3 | import "log" 4 | 5 | // Debugging 6 | const Debug = 0 7 | 8 | func DPrintf(format string, a ...interface{}) (n int, err error) { 9 | if Debug > 0 { 10 | log.Printf(format, a...) 11 | } 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /6.824-2016/src/shardkv/client.go: -------------------------------------------------------------------------------- 1 | package shardkv 2 | 3 | // 4 | // client code to talk to a sharded key/value service. 5 | // 6 | // the client first talks to the shardmaster to find out 7 | // the assignment of shards (keys) to groups, and then 8 | // talks to the group that holds the key's shard. 9 | // 10 | 11 | import "labrpc" 12 | import "crypto/rand" 13 | import "math/big" 14 | import "shardmaster" 15 | import "time" 16 | import "sync" 17 | 18 | // 19 | // which shard is a key in? 20 | // please use this function, 21 | // and please do not change it. 22 | // 23 | func key2shard(key string) int { 24 | shard := 0 25 | if len(key) > 0 { 26 | shard = int(key[0]) 27 | } 28 | shard %= shardmaster.NShards 29 | return shard 30 | } 31 | 32 | func nrand() int64 { 33 | max := big.NewInt(int64(1) << 62) 34 | bigx, _ := rand.Int(rand.Reader, max) 35 | x := bigx.Int64() 36 | return x 37 | } 38 | 39 | type Clerk struct { 40 | sm *shardmaster.Clerk 41 | config shardmaster.Config 42 | make_end func(string) *labrpc.ClientEnd 43 | // You will have to modify this struct. 44 | id int64 45 | seq int 46 | mu sync.Mutex 47 | } 48 | 49 | // 50 | // the tester calls MakeClerk. 51 | // 52 | // masters[] is needed to call shardmaster.MakeClerk(). 53 | // 54 | // make_end(servername) turns a server name from a 55 | // Config.Groups[gid][i] into a labrpc.ClientEnd on which you can 56 | // send RPCs. 57 | // 58 | func MakeClerk(masters []*labrpc.ClientEnd, make_end func(string) *labrpc.ClientEnd) *Clerk { 59 | ck := new(Clerk) 60 | ck.sm = shardmaster.MakeClerk(masters) 61 | ck.make_end = make_end 62 | // You'll have to add code here. 63 | ck.id = nrand() 64 | ck.seq = 0 65 | return ck 66 | } 67 | 68 | // 69 | // fetch the current value for a key. 70 | // returns "" if the key does not exist. 71 | // keeps trying forever in the face of all other errors. 72 | // You will have to modify this function. 73 | // 74 | func (ck *Clerk) Get(key string) string { 75 | args := GetArgs{} 76 | args.Key = key 77 | args.Id = ck.id 78 | ck.mu.Lock() 79 | args.Seq = ck.seq 80 | ck.seq++ 81 | ck.mu.Unlock() 82 | 83 | for { 84 | shard := key2shard(key) 85 | gid := ck.config.Shards[shard] 86 | if servers, ok := ck.config.Groups[gid]; ok { 87 | // try each server for the shard. 88 | for si := 0; si < len(servers); si++ { 89 | srv := ck.make_end(servers[si]) 90 | var reply GetReply 91 | ok := srv.Call("ShardKV.Get", &args, &reply) 92 | if ok && reply.WrongLeader == false && (reply.Err == OK || reply.Err == ErrNoKey) { 93 | return reply.Value 94 | } 95 | if ok && (reply.Err == ErrWrongGroup) { 96 | break 97 | } 98 | } 99 | } 100 | time.Sleep(100 * time.Millisecond) 101 | // ask master for the latest configuration. 102 | ck.config = ck.sm.Query(-1) 103 | } 104 | 105 | return "" 106 | } 107 | 108 | // 109 | // shared by Put and Append. 110 | // You will have to modify this function. 111 | // 112 | func (ck *Clerk) PutAppend(key string, value string, op string) { 113 | args := PutAppendArgs{} 114 | args.Key = key 115 | args.Value = value 116 | args.Op = op 117 | args.Id = ck.id 118 | ck.mu.Lock() 119 | args.Seq = ck.seq 120 | ck.seq++ 121 | ck.mu.Unlock() 122 | 123 | for { 124 | shard := key2shard(key) 125 | gid := ck.config.Shards[shard] 126 | if servers, ok := ck.config.Groups[gid]; ok { 127 | for si := 0; si < len(servers); si++ { 128 | srv := ck.make_end(servers[si]) 129 | var reply PutAppendReply 130 | ok := srv.Call("ShardKV.PutAppend", &args, &reply) 131 | if ok && reply.WrongLeader == false && reply.Err == OK { 132 | return 133 | } 134 | if ok && reply.Err == ErrWrongGroup { 135 | break 136 | } 137 | } 138 | } 139 | time.Sleep(100 * time.Millisecond) 140 | // ask master for the latest configuration. 141 | ck.config = ck.sm.Query(-1) 142 | } 143 | } 144 | 145 | func (ck *Clerk) Put(key string, value string) { 146 | ck.PutAppend(key, value, "Put") 147 | } 148 | func (ck *Clerk) Append(key string, value string) { 149 | ck.PutAppend(key, value, "Append") 150 | } 151 | -------------------------------------------------------------------------------- /6.824-2016/src/shardkv/common.go: -------------------------------------------------------------------------------- 1 | package shardkv 2 | 3 | import "shardmaster" 4 | 5 | // 6 | // Sharded key/value server. 7 | // Lots of replica groups, each running op-at-a-time paxos. 8 | // Shardmaster decides which group serves each shard. 9 | // Shardmaster may change shard assignment from time to time. 10 | // 11 | // You will have to modify these definitions. 12 | // 13 | 14 | const ( 15 | OK = "OK" 16 | ErrNoKey = "ErrNoKey" 17 | ErrWrongGroup = "ErrWrongGroup" 18 | 19 | ErrNotReady = "ErrNotReady" 20 | Get = "Get" 21 | Put = "Put" 22 | Append = "Append" 23 | GetShard = "GetShard" 24 | Reconfigure = "Reconfigure" 25 | ) 26 | 27 | type Err string 28 | 29 | // Put or Append 30 | type PutAppendArgs struct { 31 | // You'll have to add definitions here. 32 | Key string 33 | Value string 34 | Op string // "Put" or "Append" 35 | // You'll have to add definitions here. 36 | // Field names must start with capital letters, 37 | // otherwise RPC will break. 38 | Id int64 39 | Seq int 40 | } 41 | 42 | type PutAppendReply struct { 43 | WrongLeader bool 44 | Err Err 45 | } 46 | 47 | type GetArgs struct { 48 | Key string 49 | // You'll have to add definitions here. 50 | Id int64 51 | Seq int 52 | } 53 | 54 | type GetReply struct { 55 | WrongLeader bool 56 | Err Err 57 | Value string 58 | } 59 | 60 | type GetShardArgs struct { 61 | Shard int 62 | Config shardmaster.Config 63 | } 64 | 65 | type GetShardReply struct { 66 | Err Err 67 | Database map[string]string 68 | Ack map[int64]int 69 | WrongLeader bool 70 | } 71 | 72 | func (reply *GetShardReply) Merge(other GetShardReply) { 73 | for key := range other.Database { 74 | reply.Database[key] = other.Database[key] 75 | } 76 | for id := range other.Ack { 77 | seq, exists := reply.Ack[id] 78 | if !exists || seq < other.Ack[id] { 79 | reply.Ack[id] = other.Ack[id] 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /6.824-2016/src/shardmaster/client.go: -------------------------------------------------------------------------------- 1 | package shardmaster 2 | 3 | // 4 | // Shardmaster clerk. 5 | // 6 | 7 | import "labrpc" 8 | import "time" 9 | import "crypto/rand" 10 | import "math/big" 11 | import "sync" 12 | 13 | type Clerk struct { 14 | servers []*labrpc.ClientEnd 15 | // Your data here. 16 | id int64 17 | seq int 18 | mu sync.Mutex 19 | } 20 | 21 | func nrand() int64 { 22 | max := big.NewInt(int64(1) << 62) 23 | bigx, _ := rand.Int(rand.Reader, max) 24 | x := bigx.Int64() 25 | return x 26 | } 27 | 28 | func MakeClerk(servers []*labrpc.ClientEnd) *Clerk { 29 | ck := new(Clerk) 30 | ck.servers = servers 31 | // Your code here. 32 | ck.id = nrand() 33 | ck.seq = 0 34 | return ck 35 | } 36 | 37 | func (ck *Clerk) Query(num int) Config { 38 | args := &QueryArgs{} 39 | // Your code here. 40 | args.Num = num 41 | args.Id = ck.id 42 | ck.mu.Lock() 43 | args.Seq = ck.seq 44 | ck.seq++ 45 | ck.mu.Unlock() 46 | 47 | for { 48 | // try each known server. 49 | for _, srv := range ck.servers { 50 | var reply QueryReply 51 | ok := srv.Call("ShardMaster.Query", args, &reply) 52 | if ok && reply.WrongLeader == false { 53 | return reply.Config 54 | } 55 | } 56 | time.Sleep(100 * time.Millisecond) 57 | } 58 | } 59 | 60 | func (ck *Clerk) Join(servers map[int][]string) { 61 | args := &JoinArgs{} 62 | // Your code here. 63 | args.Servers = servers 64 | args.Id = ck.id 65 | ck.mu.Lock() 66 | args.Seq = ck.seq 67 | ck.seq++ 68 | ck.mu.Unlock() 69 | 70 | for { 71 | // try each known server. 72 | for _, srv := range ck.servers { 73 | var reply JoinReply 74 | ok := srv.Call("ShardMaster.Join", args, &reply) 75 | if ok && reply.WrongLeader == false { 76 | return 77 | } 78 | } 79 | time.Sleep(100 * time.Millisecond) 80 | } 81 | } 82 | 83 | func (ck *Clerk) Leave(gids []int) { 84 | args := &LeaveArgs{} 85 | // Your code here. 86 | args.GIDs = gids 87 | args.Id = ck.id 88 | ck.mu.Lock() 89 | args.Seq = ck.seq 90 | ck.seq++ 91 | ck.mu.Unlock() 92 | 93 | for { 94 | // try each known server. 95 | for _, srv := range ck.servers { 96 | var reply LeaveReply 97 | ok := srv.Call("ShardMaster.Leave", args, &reply) 98 | if ok && reply.WrongLeader == false { 99 | return 100 | } 101 | } 102 | time.Sleep(100 * time.Millisecond) 103 | } 104 | } 105 | 106 | func (ck *Clerk) Move(shard int, gid int) { 107 | args := &MoveArgs{} 108 | // Your code here. 109 | args.Shard = shard 110 | args.GID = gid 111 | args.Id = ck.id 112 | ck.mu.Lock() 113 | args.Seq = ck.seq 114 | ck.seq++ 115 | ck.mu.Unlock() 116 | 117 | for { 118 | // try each known server. 119 | for _, srv := range ck.servers { 120 | var reply MoveReply 121 | ok := srv.Call("ShardMaster.Move", args, &reply) 122 | if ok && reply.WrongLeader == false { 123 | return 124 | } 125 | } 126 | time.Sleep(100 * time.Millisecond) 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /6.824-2016/src/shardmaster/common.go: -------------------------------------------------------------------------------- 1 | package shardmaster 2 | 3 | // 4 | // Master shard server: assigns shards to replication groups. 5 | // 6 | // RPC interface: 7 | // Join(servers) -- add a set of groups (gid -> server-list mapping). 8 | // Leave(gids) -- delete a set of groups. 9 | // Move(shard, gid) -- hand off one shard from current owner to gid. 10 | // Query(num) -> fetch Config # num, or latest config if num==-1. 11 | // 12 | // A Config (configuration) describes a set of replica groups, and the 13 | // replica group responsible for each shard. Configs are numbered. Config 14 | // #0 is the initial configuration, with no groups and all shards 15 | // assigned to group 0 (the invalid group). 16 | // 17 | // A GID is a replica group ID. GIDs must be uniqe and > 0. 18 | // Once a GID joins, and leaves, it should never join again. 19 | // 20 | // You will need to add fields to the RPC arguments. 21 | // 22 | 23 | // The number of shards. 24 | const NShards = 10 25 | 26 | // A configuration -- an assignment of shards to groups. 27 | // Please don't change this. 28 | type Config struct { 29 | Num int // config number 30 | Shards [NShards]int // shard -> gid 31 | Groups map[int][]string // gid -> servers[] 32 | } 33 | 34 | const ( 35 | OK = "OK" 36 | 37 | Join = "Join" 38 | Leave = "Leave" 39 | Move = "Move" 40 | Query = "Query" 41 | ) 42 | 43 | type Err string 44 | 45 | type JoinArgs struct { 46 | Servers map[int][]string // new GID -> servers mappings 47 | Id int64 48 | Seq int 49 | } 50 | 51 | type JoinReply struct { 52 | WrongLeader bool 53 | Err Err 54 | } 55 | 56 | type LeaveArgs struct { 57 | GIDs []int 58 | Id int64 59 | Seq int 60 | } 61 | 62 | type LeaveReply struct { 63 | WrongLeader bool 64 | Err Err 65 | } 66 | 67 | type MoveArgs struct { 68 | Shard int 69 | GID int 70 | Id int64 71 | Seq int 72 | } 73 | 74 | type MoveReply struct { 75 | WrongLeader bool 76 | Err Err 77 | } 78 | 79 | type QueryArgs struct { 80 | Num int // desired config number 81 | Id int64 82 | Seq int 83 | } 84 | 85 | type QueryReply struct { 86 | WrongLeader bool 87 | Err Err 88 | Config Config 89 | } 90 | -------------------------------------------------------------------------------- /6.824-2016/src/viewservice/client.go: -------------------------------------------------------------------------------- 1 | package viewservice 2 | 3 | import "net/rpc" 4 | import "fmt" 5 | 6 | // 7 | // the viewservice Clerk lives in the client 8 | // and maintains a little state. 9 | // 10 | type Clerk struct { 11 | me string // client's name (host:port) 12 | server string // viewservice's host:port 13 | } 14 | 15 | func MakeClerk(me string, server string) *Clerk { 16 | ck := new(Clerk) 17 | ck.me = me 18 | ck.server = server 19 | return ck 20 | } 21 | 22 | // 23 | // call() sends an RPC to the rpcname handler on server srv 24 | // with arguments args, waits for the reply, and leaves the 25 | // reply in reply. the reply argument should be a pointer 26 | // to a reply structure. 27 | // 28 | // the return value is true if the server responded, and false 29 | // if call() was not able to contact the server. in particular, 30 | // the reply's contents are only valid if call() returned true. 31 | // 32 | // you should assume that call() will return an 33 | // error after a while if the server is dead. 34 | // don't provide your own time-out mechanism. 35 | // 36 | // please use call() to send all RPCs, in client.go and server.go. 37 | // please don't change this function. 38 | // 39 | func call(srv string, rpcname string, 40 | args interface{}, reply interface{}) bool { 41 | c, errx := rpc.Dial("unix", srv) 42 | if errx != nil { 43 | return false 44 | } 45 | defer c.Close() 46 | 47 | err := c.Call(rpcname, args, reply) 48 | if err == nil { 49 | return true 50 | } 51 | 52 | fmt.Println(err) 53 | return false 54 | } 55 | 56 | func (ck *Clerk) Ping(viewnum uint) (View, error) { 57 | // prepare the arguments. 58 | args := &PingArgs{} 59 | args.Me = ck.me 60 | args.Viewnum = viewnum 61 | var reply PingReply 62 | 63 | // send an RPC request, wait for the reply. 64 | ok := call(ck.server, "ViewServer.Ping", args, &reply) 65 | if ok == false { 66 | return View{}, fmt.Errorf("Ping(%v) failed", viewnum) 67 | } 68 | 69 | return reply.View, nil 70 | } 71 | 72 | func (ck *Clerk) Get() (View, bool) { 73 | args := &GetArgs{} 74 | var reply GetReply 75 | ok := call(ck.server, "ViewServer.Get", args, &reply) 76 | if ok == false { 77 | return View{}, false 78 | } 79 | return reply.View, true 80 | } 81 | 82 | func (ck *Clerk) Primary() string { 83 | v, ok := ck.Get() 84 | if ok { 85 | return v.Primary 86 | } 87 | return "" 88 | } 89 | -------------------------------------------------------------------------------- /6.824-2016/src/viewservice/common.go: -------------------------------------------------------------------------------- 1 | package viewservice 2 | 3 | import "time" 4 | 5 | // 6 | // This is a non-replicated view service for a simple 7 | // primary/backup system. 8 | // 9 | // The view service goes through a sequence of numbered 10 | // views, each with a primary and (if possible) a backup. 11 | // A view consists of a view number and the host:port of 12 | // the view's primary and backup p/b servers. 13 | // 14 | // The primary in a view is always either the primary 15 | // or the backup of the previous view (in order to ensure 16 | // that the p/b service's state is preserved). 17 | // 18 | // Each p/b server should send a Ping RPC once per PingInterval. 19 | // The view server replies with a description of the current 20 | // view. The Pings let the view server know that the p/b 21 | // server is still alive; inform the p/b server of the current 22 | // view; and inform the view server of the most recent view 23 | // that the p/b server knows about. 24 | // 25 | // The view server proceeds to a new view when either it hasn't 26 | // received a ping from the primary or backup for a while, or 27 | // if there was no backup and a new server starts Pinging. 28 | // 29 | // The view server will not proceed to a new view until 30 | // the primary from the current view acknowledges 31 | // that it is operating in the current view. This helps 32 | // ensure that there's at most one p/b primary operating at 33 | // a time. 34 | // 35 | 36 | type View struct { 37 | Viewnum uint 38 | Primary string 39 | Backup string 40 | } 41 | 42 | // clients should send a Ping RPC this often, 43 | // to tell the viewservice that the client is alive. 44 | const PingInterval = time.Millisecond * 100 45 | 46 | // the viewserver will declare a client dead if it misses 47 | // this many Ping RPCs in a row. 48 | const DeadPings = 5 49 | 50 | // 51 | // Ping(): called by a primary/backup server to tell the 52 | // view service it is alive, to indicate whether p/b server 53 | // has seen the latest view, and for p/b server to learn 54 | // the latest view. 55 | // 56 | // If Viewnum is zero, the caller is signalling that it is 57 | // alive and could become backup if needed. 58 | // 59 | 60 | type PingArgs struct { 61 | Me string // "host:port" 62 | Viewnum uint // caller's notion of current view # 63 | } 64 | 65 | type PingReply struct { 66 | View View 67 | } 68 | 69 | // 70 | // Get(): fetch the current view, without volunteering 71 | // to be a server. mostly for clients of the p/b service, 72 | // and for testing. 73 | // 74 | 75 | type GetArgs struct { 76 | } 77 | 78 | type GetReply struct { 79 | View View 80 | } 81 | -------------------------------------------------------------------------------- /6.824-2016/src/viewservice/server.go: -------------------------------------------------------------------------------- 1 | package viewservice 2 | 3 | import "net" 4 | import "net/rpc" 5 | import "log" 6 | import "time" 7 | import "sync" 8 | import "fmt" 9 | import "os" 10 | import "sync/atomic" 11 | 12 | type ViewServer struct { 13 | mu sync.Mutex 14 | l net.Listener 15 | dead int32 // for testing 16 | rpccount int32 // for testing 17 | me string 18 | 19 | // Your declarations here. 20 | } 21 | 22 | // 23 | // server Ping RPC handler. 24 | // 25 | func (vs *ViewServer) Ping(args *PingArgs, reply *PingReply) error { 26 | 27 | // Your code here. 28 | 29 | return nil 30 | } 31 | 32 | // 33 | // server Get() RPC handler. 34 | // 35 | func (vs *ViewServer) Get(args *GetArgs, reply *GetReply) error { 36 | 37 | // Your code here. 38 | 39 | return nil 40 | } 41 | 42 | // 43 | // tick() is called once per PingInterval; it should notice 44 | // if servers have died or recovered, and change the view 45 | // accordingly. 46 | // 47 | func (vs *ViewServer) tick() { 48 | 49 | // Your code here. 50 | } 51 | 52 | // 53 | // tell the server to shut itself down. 54 | // for testing. 55 | // please don't change these two functions. 56 | // 57 | func (vs *ViewServer) Kill() { 58 | atomic.StoreInt32(&vs.dead, 1) 59 | vs.l.Close() 60 | } 61 | 62 | // 63 | // has this server been asked to shut down? 64 | // 65 | func (vs *ViewServer) isdead() bool { 66 | return atomic.LoadInt32(&vs.dead) != 0 67 | } 68 | 69 | // please don't change this function. 70 | func (vs *ViewServer) GetRPCCount() int32 { 71 | return atomic.LoadInt32(&vs.rpccount) 72 | } 73 | 74 | func StartServer(me string) *ViewServer { 75 | vs := new(ViewServer) 76 | vs.me = me 77 | // Your vs.* initializations here. 78 | 79 | // tell net/rpc about our RPC server and handlers. 80 | rpcs := rpc.NewServer() 81 | rpcs.Register(vs) 82 | 83 | // prepare to receive connections from clients. 84 | // change "unix" to "tcp" to use over a network. 85 | os.Remove(vs.me) // only needed for "unix" 86 | l, e := net.Listen("unix", vs.me) 87 | if e != nil { 88 | log.Fatal("listen error: ", e) 89 | } 90 | vs.l = l 91 | 92 | // please don't change any of the following code, 93 | // or do anything to subvert it. 94 | 95 | // create a thread to accept RPC connections from clients. 96 | go func() { 97 | for vs.isdead() == false { 98 | conn, err := vs.l.Accept() 99 | if err == nil && vs.isdead() == false { 100 | atomic.AddInt32(&vs.rpccount, 1) 101 | go rpcs.ServeConn(conn) 102 | } else if err == nil { 103 | conn.Close() 104 | } 105 | if err != nil && vs.isdead() == false { 106 | fmt.Printf("ViewServer(%v) accept: %v\n", me, err.Error()) 107 | vs.Kill() 108 | } 109 | } 110 | }() 111 | 112 | // create a thread to call tick() periodically. 113 | go func() { 114 | for vs.isdead() == false { 115 | vs.tick() 116 | time.Sleep(PingInterval) 117 | } 118 | }() 119 | 120 | return vs 121 | } 122 | -------------------------------------------------------------------------------- /6.824-2016/src/viewservice/test_test.go: -------------------------------------------------------------------------------- 1 | package viewservice 2 | 3 | import "testing" 4 | import "runtime" 5 | import "time" 6 | import "fmt" 7 | import "os" 8 | import "strconv" 9 | 10 | func check(t *testing.T, ck *Clerk, p string, b string, n uint) { 11 | view, _ := ck.Get() 12 | if view.Primary != p { 13 | t.Fatalf("wanted primary %v, got %v", p, view.Primary) 14 | } 15 | if view.Backup != b { 16 | t.Fatalf("wanted backup %v, got %v", b, view.Backup) 17 | } 18 | if n != 0 && n != view.Viewnum { 19 | t.Fatalf("wanted viewnum %v, got %v", n, view.Viewnum) 20 | } 21 | if ck.Primary() != p { 22 | t.Fatalf("wanted primary %v, got %v", p, ck.Primary()) 23 | } 24 | } 25 | 26 | func port(suffix string) string { 27 | s := "/var/tmp/824-" 28 | s += strconv.Itoa(os.Getuid()) + "/" 29 | os.Mkdir(s, 0777) 30 | s += "viewserver-" 31 | s += strconv.Itoa(os.Getpid()) + "-" 32 | s += suffix 33 | return s 34 | } 35 | 36 | func Test1(t *testing.T) { 37 | runtime.GOMAXPROCS(4) 38 | 39 | vshost := port("v") 40 | vs := StartServer(vshost) 41 | 42 | ck1 := MakeClerk(port("1"), vshost) 43 | ck2 := MakeClerk(port("2"), vshost) 44 | ck3 := MakeClerk(port("3"), vshost) 45 | 46 | // 47 | 48 | if ck1.Primary() != "" { 49 | t.Fatalf("there was a primary too soon") 50 | } 51 | 52 | // very first primary 53 | fmt.Printf("Test: First primary ...\n") 54 | 55 | for i := 0; i < DeadPings*2; i++ { 56 | view, _ := ck1.Ping(0) 57 | if view.Primary == ck1.me { 58 | break 59 | } 60 | time.Sleep(PingInterval) 61 | } 62 | check(t, ck1, ck1.me, "", 1) 63 | fmt.Printf(" ... Passed\n") 64 | 65 | // very first backup 66 | fmt.Printf("Test: First backup ...\n") 67 | 68 | { 69 | vx, _ := ck1.Get() 70 | for i := 0; i < DeadPings*2; i++ { 71 | ck1.Ping(1) 72 | view, _ := ck2.Ping(0) 73 | if view.Backup == ck2.me { 74 | break 75 | } 76 | time.Sleep(PingInterval) 77 | } 78 | check(t, ck1, ck1.me, ck2.me, vx.Viewnum+1) 79 | } 80 | fmt.Printf(" ... Passed\n") 81 | 82 | // primary dies, backup should take over 83 | fmt.Printf("Test: Backup takes over if primary fails ...\n") 84 | 85 | { 86 | ck1.Ping(2) 87 | vx, _ := ck2.Ping(2) 88 | for i := 0; i < DeadPings*2; i++ { 89 | v, _ := ck2.Ping(vx.Viewnum) 90 | if v.Primary == ck2.me && v.Backup == "" { 91 | break 92 | } 93 | time.Sleep(PingInterval) 94 | } 95 | check(t, ck2, ck2.me, "", vx.Viewnum+1) 96 | } 97 | fmt.Printf(" ... Passed\n") 98 | 99 | // revive ck1, should become backup 100 | fmt.Printf("Test: Restarted server becomes backup ...\n") 101 | 102 | { 103 | vx, _ := ck2.Get() 104 | ck2.Ping(vx.Viewnum) 105 | for i := 0; i < DeadPings*2; i++ { 106 | ck1.Ping(0) 107 | v, _ := ck2.Ping(vx.Viewnum) 108 | if v.Primary == ck2.me && v.Backup == ck1.me { 109 | break 110 | } 111 | time.Sleep(PingInterval) 112 | } 113 | check(t, ck2, ck2.me, ck1.me, vx.Viewnum+1) 114 | } 115 | fmt.Printf(" ... Passed\n") 116 | 117 | // start ck3, kill the primary (ck2), the previous backup (ck1) 118 | // should become the server, and ck3 the backup. 119 | // this should happen in a single view change, without 120 | // any period in which there's no backup. 121 | fmt.Printf("Test: Idle third server becomes backup if primary fails ...\n") 122 | 123 | { 124 | vx, _ := ck2.Get() 125 | ck2.Ping(vx.Viewnum) 126 | for i := 0; i < DeadPings*2; i++ { 127 | ck3.Ping(0) 128 | v, _ := ck1.Ping(vx.Viewnum) 129 | if v.Primary == ck1.me && v.Backup == ck3.me { 130 | break 131 | } 132 | vx = v 133 | time.Sleep(PingInterval) 134 | } 135 | check(t, ck1, ck1.me, ck3.me, vx.Viewnum+1) 136 | } 137 | fmt.Printf(" ... Passed\n") 138 | 139 | // kill and immediately restart the primary -- does viewservice 140 | // conclude primary is down even though it's pinging? 141 | fmt.Printf("Test: Restarted primary treated as dead ...\n") 142 | 143 | { 144 | vx, _ := ck1.Get() 145 | ck1.Ping(vx.Viewnum) 146 | for i := 0; i < DeadPings*2; i++ { 147 | ck1.Ping(0) 148 | ck3.Ping(vx.Viewnum) 149 | v, _ := ck3.Get() 150 | if v.Primary != ck1.me { 151 | break 152 | } 153 | time.Sleep(PingInterval) 154 | } 155 | vy, _ := ck3.Get() 156 | if vy.Primary != ck3.me { 157 | t.Fatalf("expected primary=%v, got %v\n", ck3.me, vy.Primary) 158 | } 159 | } 160 | fmt.Printf(" ... Passed\n") 161 | 162 | fmt.Printf("Test: Dead backup is removed from view ...\n") 163 | 164 | // set up a view with just 3 as primary, 165 | // to prepare for the next test. 166 | { 167 | for i := 0; i < DeadPings*3; i++ { 168 | vx, _ := ck3.Get() 169 | ck3.Ping(vx.Viewnum) 170 | time.Sleep(PingInterval) 171 | } 172 | v, _ := ck3.Get() 173 | if v.Primary != ck3.me || v.Backup != "" { 174 | t.Fatalf("wrong primary or backup") 175 | } 176 | } 177 | fmt.Printf(" ... Passed\n") 178 | 179 | // does viewserver wait for ack of previous view before 180 | // starting the next one? 181 | fmt.Printf("Test: Viewserver waits for primary to ack view ...\n") 182 | 183 | { 184 | // set up p=ck3 b=ck1, but 185 | // but do not ack 186 | vx, _ := ck1.Get() 187 | for i := 0; i < DeadPings*3; i++ { 188 | ck1.Ping(0) 189 | ck3.Ping(vx.Viewnum) 190 | v, _ := ck1.Get() 191 | if v.Viewnum > vx.Viewnum { 192 | break 193 | } 194 | time.Sleep(PingInterval) 195 | } 196 | check(t, ck1, ck3.me, ck1.me, vx.Viewnum+1) 197 | vy, _ := ck1.Get() 198 | // ck3 is the primary, but it never acked. 199 | // let ck3 die. check that ck1 is not promoted. 200 | for i := 0; i < DeadPings*3; i++ { 201 | v, _ := ck1.Ping(vy.Viewnum) 202 | if v.Viewnum > vy.Viewnum { 203 | break 204 | } 205 | time.Sleep(PingInterval) 206 | } 207 | check(t, ck2, ck3.me, ck1.me, vy.Viewnum) 208 | } 209 | fmt.Printf(" ... Passed\n") 210 | 211 | // if old servers die, check that a new (uninitialized) server 212 | // cannot take over. 213 | fmt.Printf("Test: Uninitialized server can't become primary ...\n") 214 | 215 | { 216 | for i := 0; i < DeadPings*2; i++ { 217 | v, _ := ck1.Get() 218 | ck1.Ping(v.Viewnum) 219 | ck2.Ping(0) 220 | ck3.Ping(v.Viewnum) 221 | time.Sleep(PingInterval) 222 | } 223 | for i := 0; i < DeadPings*2; i++ { 224 | ck2.Ping(0) 225 | time.Sleep(PingInterval) 226 | } 227 | vz, _ := ck2.Get() 228 | if vz.Primary == ck2.me { 229 | t.Fatalf("uninitialized backup promoted to primary") 230 | } 231 | } 232 | fmt.Printf(" ... Passed\n") 233 | 234 | vs.Kill() 235 | } 236 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mit-courses 2 | [`6.824: Distributed Systems Spring 2015`](http://nil.csail.mit.edu/6.824/2015/index.html)
3 | [`6.824: Distributed Systems Spring 2016`](http://nil.csail.mit.edu/6.824/2016/index.html) 4 | --------------------------------------------------------------------------------