github.com/DARA-Project/GoDist-Scheduler@v0.0.0-20201030134746-668de4acea0d/structured/rsm/kvservicemain.go (about) 1 // A simple key-value store that supports four API calls over RPC: 2 // val <- get(key,clientid,clock): execute k-v get from clientid with lamport clock value 3 // - put(key,val,clientid,clock) : execute k-v put from clientid with lamport clock value 4 // - clockupdate(clientid,clock) : report latest lamport clock value at clientid 5 // - disconnect(clientid) : report disconnection/failure of clientid 6 // 7 // Usage: go run kvservicemain.go [ip:port] [num-clients] 8 // 9 // - [ip:port] : the ip and TCP port on which the service will listen 10 // for connections 11 // 12 // - [num-clients] : Number of unique clients to expect (unique clientid's) 13 14 package main 15 16 import ( 17 "fmt" 18 "log" 19 // "math/rand" 20 "net" 21 "net/rpc" 22 "os" 23 "strconv" 24 "sync" 25 "time" 26 ) 27 28 // args in get(args) 29 type GetArgs struct { 30 Key string // key to look up 31 Clientid uint8 // client id issuing this get 32 Clock uint64 // value of lamport clock at the issuing client 33 } 34 35 // args in put(args) 36 type PutArgs struct { 37 Key string // key to associate value with 38 Val string // value 39 Clientid uint8 // client id issueing this put 40 Clock uint64 // value of lamport clock at the issuing client 41 } 42 43 // args in clockupdate(args) 44 type ClockUpdateArgs struct { 45 Clientid uint8 // client id issueing this put 46 Clock uint64 // value of lamport clock at the issuing client 47 } 48 49 // args in disconnect(args) 50 type DisconnectArgs struct { 51 Clientid uint8 // client id issueing this put 52 } 53 54 // Reply from service for all the API calls above. 55 type ValReply struct { 56 Val string // value; depends on the call 57 } 58 59 // Value in the key-val store. 60 type MapVal struct { 61 value string // the underlying value representation 62 } 63 64 type ClientInfo struct { 65 Clock uint64 66 Alive bool 67 LastReq uint64 68 } 69 70 // Map implementing the key-value store: use this for k-v storage. 71 var kvmap map[string]*MapVal 72 var kvMux sync.Mutex 73 74 var clientinfomap map[uint8]*ClientInfo 75 var cliMux sync.Mutex 76 77 type KeyValService int 78 79 // Command line arg. 80 var numClients uint8 81 82 func updateAddClientClock(clientID uint8, clock uint64) { 83 if client, ok := clientinfomap[clientID]; ok { 84 client.Clock = clock 85 clientinfomap[clientID] = client 86 } else { 87 client := &ClientInfo{clock, true, 0} 88 clientinfomap[clientID] = client 89 } 90 } 91 92 func addClientRequestInfo(clientID uint8, request uint64) { 93 if client, ok := clientinfomap[clientID]; ok { 94 client.LastReq = request 95 clientinfomap[clientID] = client 96 } 97 } 98 99 func deleteClientRequestInfo(clientID uint8) { 100 if client, ok := clientinfomap[clientID]; ok { 101 client.LastReq = 0 102 clientinfomap[clientID] = client 103 } 104 } 105 106 func check_stability(clientID uint8, clock uint64) bool { 107 var clients_checked uint8 108 clients_checked = 0 109 for cid, client := range clientinfomap { 110 // Don't check for dead clients for stability 111 if client.Alive == false { 112 clients_checked += 1 113 continue 114 } 115 if clientID == cid { 116 clients_checked += 1 117 continue 118 } 119 // Check if the client has a lower or equal clock so we must return as this request is not stable yet. 120 if client.Clock <= clock { 121 return false 122 } 123 // Check if any client has an earlier pending request which should be served first 124 if client.LastReq != 0 && client.LastReq < clock { 125 return false 126 } 127 // Tie-breaker rule 128 if client.LastReq == clock && cid < clientID { 129 return false 130 } 131 clients_checked += 1 132 } 133 // Message is not stable if we have not received a single other message from any other client. 134 if clients_checked != numClients { 135 return false 136 } 137 return true 138 } 139 140 func returnWhenStable(clientID uint8, clock uint64) { 141 is_stable := false 142 for { 143 if is_stable { 144 break 145 } 146 ticker := time.NewTicker(100 * time.Millisecond) 147 select { 148 case <- ticker.C: 149 cliMux.Lock() 150 is_stable = check_stability(clientID, clock) 151 cliMux.Unlock() 152 } 153 } 154 } 155 156 // GET 157 func (kvs *KeyValService) Get(args *GetArgs, reply *ValReply) error { 158 clientID := args.Clientid 159 clock := args.Clock 160 log.Printf("[GET-R] Client %d Clock %d\n", clientID, clock) 161 cliMux.Lock() 162 updateAddClientClock(clientID, clock) 163 addClientRequestInfo(clientID, clock) 164 cliMux.Unlock() 165 returnWhenStable(clientID, clock) 166 kvMux.Lock() 167 key := args.Key 168 if v, ok := kvmap[key]; ok { 169 reply.Val = v.value 170 } else { 171 reply.Val = "" 172 } 173 kvMux.Unlock() 174 cliMux.Lock() 175 deleteClientRequestInfo(clientID) 176 cliMux.Unlock() 177 log.Printf("[GET-S] Client %d Clock %d\n", clientID, clock) 178 return nil 179 } 180 181 // PUT 182 func (kvs *KeyValService) Put(args *PutArgs, reply *ValReply) error { 183 clientID := args.Clientid 184 clock := args.Clock 185 log.Printf("[PUT-R] Client %d Clock %d\n", clientID, clock) 186 cliMux.Lock() 187 updateAddClientClock(clientID, clock) 188 addClientRequestInfo(clientID, clock) 189 cliMux.Unlock() 190 returnWhenStable(clientID, clock) 191 kvMux.Lock() 192 key := args.Key 193 val := args.Val 194 mapVal := &MapVal{val} 195 kvmap[key] = mapVal 196 reply.Val = "" 197 kvMux.Unlock() 198 cliMux.Lock() 199 deleteClientRequestInfo(clientID) 200 cliMux.Unlock() 201 log.Printf("[PUT-S] Client %d Clock %d\n", clientID, clock) 202 return nil 203 } 204 205 // CLOCKUPDATE 206 func (kvs *KeyValService) ClockUpdate(args *ClockUpdateArgs, reply *ValReply) error { 207 clientID := args.Clientid 208 clock := args.Clock 209 //log.Printf("[UPD-R] Client %d Clock %d\n", clientID, clock) 210 cliMux.Lock() 211 updateAddClientClock(clientID, clock) 212 cliMux.Unlock() 213 //log.Printf("[UPD-S] Client %d Clock %d\n", clientID, clock) 214 return nil 215 } 216 217 // DISCONNECT 218 func (kvs *KeyValService) Disconnect(args *DisconnectArgs, reply *ValReply) error { 219 clientID := args.Clientid 220 log.Printf("[DSN-R] Client %d\n", clientID) 221 cliMux.Lock() 222 if client, ok := clientinfomap[clientID]; ok { 223 client.Alive = false 224 clientinfomap[clientID] = client 225 } 226 reply.Val = "" 227 cliMux.Unlock() 228 log.Printf("[DSN-S] Client %d\n", clientID) 229 return nil 230 } 231 232 // Main server loop. 233 func main() { 234 // Parse args. 235 usage := fmt.Sprintf("Usage: %s [ip:port] [num-clients]\n", os.Args[0]) 236 if len(os.Args) != 3 { 237 fmt.Printf(usage) 238 os.Exit(1) 239 } 240 241 ip_port := os.Args[1] 242 arg, err := strconv.ParseUint(os.Args[2], 10, 8) 243 if err != nil { 244 fmt.Println(err) 245 os.Exit(1) 246 } 247 if arg == 0 { 248 fmt.Printf(usage) 249 fmt.Printf("\tnum-clients arg must be non-zero\n") 250 os.Exit(1) 251 } 252 numClients = uint8(arg) 253 254 // Setup key-value store and register service. 255 kvservice := new(KeyValService) 256 rpc.Register(kvservice) 257 l, e := net.Listen("tcp", ip_port) 258 if e != nil { 259 log.Fatal("listen error:", e) 260 } 261 262 // Init maps 263 kvmap = map[string]*MapVal{} 264 clientinfomap = map[uint8]*ClientInfo{} 265 266 // TODO: Enter servicing loop, like: 267 268 // for { 269 // conn, _ := l.Accept() 270 // go rpc.ServeConn(conn) 271 // } 272 log.Println("Business is open") 273 for { 274 conn, _ := l.Accept() 275 go rpc.ServeConn(conn) 276 } 277 } 278