//Now it is not Multi-Paxos, but just Paxos //Each command slot has its own na,nh,v, or say, version control func (pn *paxosNode) Prepare(args *paxosrpc.PrepareArgs, reply *paxosrpc.PrepareReply) error { LOGV.Printf("node %d OnPrepare:%d %s %d\n", pn.nodeID, args.SlotIdx, args.V.ToString(), args.N) LOGV2.Printf("node %d get lock in Prepare()\n", pn.nodeID) pn.cmdMutex.Lock() v, ok := pn.tempSlots[args.SlotIdx] if ok { if args.N <= v.Nh { //n<nh reply.Status = paxosrpc.Reject //to speed up, return the Nh the node have seen //TODO:now it is not used, but we should consider it when we want to optimize the system a little bit reply.Na = v.Nh } else { if v.isAccepted || v.isCommited { //accepted or commited state v.Nh = args.N pn.tempSlots[args.SlotIdx] = v reply.Status = paxosrpc.Existed reply.Na = v.Na reply.Va = v.V } else { //prepare state ic := IndexCommand{} ic.Index = args.SlotIdx ic.Na = -1 ic.Nh = args.N ic.isAccepted = false ic.isCommited = false pn.tempSlots[args.SlotIdx] = ic reply.Status = paxosrpc.OK reply.Na = ic.Na reply.Va = ic.V } } } else { //empty slot ic := IndexCommand{} ic.Index = args.SlotIdx ic.Na = -1 ic.Nh = args.N ic.isAccepted = false ic.isCommited = false pn.tempSlots[args.SlotIdx] = ic reply.Status = paxosrpc.OK reply.Na = ic.Na reply.Va = ic.V } LOGV2.Printf("node %d release lock in Prepare()\n", pn.nodeID) pn.cmdMutex.Unlock() LOGV.Printf("node %d leaving OnPrepare(%d %s %d)\n\t[%d %d %s]\n", pn.nodeID, args.SlotIdx, args.V.ToString(), args.N, reply.Status, reply.Na, reply.Va.ToString()) return nil }
func (pn *paxosNode) DoPrepare(args *paxosrpc.PrepareArgs, reply *paxosrpc.PrepareReply) error { LOGV.Printf("node %d DoPrepare:%d %s %d\n", pn.nodeID, args.SlotIdx, args.V.ToString(), args.N) replychan := make(chan *paxosrpc.PrepareReply, len(pn.peers)) for i, n := range pn.peers { go func(idx int, peernode Node) { r := paxosrpc.PrepareReply{} //if localhost, call locally if peernode.HostPort == pn.addrport { pn.Prepare(args, &r) replychan <- &r } else { //else, call rpc peer, err := rpcwrapper.DialHTTP("tcp", peernode.HostPort) if err != nil { LOGE.Printf("node %d Cannot reach peer %d:%s\n", pn.nodeID, idx, peernode.HostPort) r.Status = paxosrpc.Reject replychan <- &r return } prepareCall := peer.Go("PaxosNode.Prepare", args, &r, nil) select { case _, _ = <-prepareCall.Done: replychan <- &r case _ = <-time.After(time.Second): //TODO: how to handle timeout correctly? r.Status = paxosrpc.Reject replychan <- &r } peer.Close() } }(i, n) } numOK := 0 numRej := 0 reply.Na = -1 for num := 0; num < len(pn.peers); num++ { r, _ := <-replychan if r.Status != paxosrpc.Reject { numOK++ if r.Status == paxosrpc.Existed && r.Na > reply.Na { reply.Na = r.Na reply.Va = r.Va } } else { numRej++ } } if reply.Na == -1 { reply.Na = args.N reply.Va = args.V } LOGV.Printf("node %d DoPrepare %d result:%s %d[%dOK %dRej]\n", pn.nodeID, args.SlotIdx, reply.Va.ToString(), reply.Na, numOK, numRej) if numOK > len(pn.peers)/2 { reply.Status = paxosrpc.OK return nil //return nil error, and let caller to do the accept step } else { reply.Status = paxosrpc.Reject return nil } }