예제 #1
0
파일: paxos_impl.go 프로젝트: postfix/gobby
//command is the command the app/paxos want to commit/nop
//iter is the current iter round, a new command use this to increase its own N
//index is the index of commitedCommands. When we want to commit a new command, set
//index=-1, and when we want to fill the gap in slot i, sent index=i.
//Now the functon return three values. The first is a boolean varible indicating the
//current Paxos is success or not. The second is a boolean indicating the current
//command is commited or rejected. The third is the current slot's highest Vh.
func (pn *paxosNode) DoReplicate(command *command.Command, iter, index int) (bool, bool, int) {
	//Prepare
	prepareArgs := paxosrpc.PrepareArgs{}
	/*if index == -1 {
		LOGV2.Printf("node %d get lock in DoReplicate()\n", pn.nodeID)
		pn.cmdMutex.Lock()
		prepareArgs.SlotIdx = len(pn.commitedCommands)
		LOGV2.Printf("node %d release lock in DoReplicate()\n", pn.nodeID)
		pn.cmdMutex.Unlock()
	} else {
		prepareArgs.SlotIdx = index
	}*/
	prepareArgs.SlotIdx = index
	prepareArgs.N = pn.nodeID + iter*pn.numNodes
	prepareArgs.V = *command

	prepareReply := paxosrpc.PrepareReply{}
	prepareReply.Status = paxosrpc.Reject

	pn.DoPrepare(&prepareArgs, &prepareReply)
	if prepareReply.Status == paxosrpc.Reject {
		if prepareReply.Na > prepareArgs.N {
			return false, false, prepareReply.Na
		} else {
			return false, false, prepareArgs.N
		}
	}

	//Accept
	acceptArgs := paxosrpc.AcceptArgs{}
	acceptArgs.SlotIdx = prepareArgs.SlotIdx
	LOGV.Printf("Before DoAccept:%d %d\n", acceptArgs.SlotIdx, acceptArgs.N)
	//TODO:need check, maybe wrong
	if prepareReply.Na == prepareArgs.N {
		acceptArgs.V = *command
	} else {
		acceptArgs.V = prepareReply.Va
	}
	acceptArgs.N = prepareArgs.N
	acceptReply := paxosrpc.AcceptReply{}
	pn.DoAccept(&acceptArgs, &acceptReply)

	if acceptReply.Status == paxosrpc.Reject {
		return false, false, acceptArgs.N
	}

	//Commit
	commitArgs := paxosrpc.CommitArgs{}
	commitArgs.SlotIdx = prepareArgs.SlotIdx
	commitArgs.N = acceptArgs.N
	commitArgs.V = acceptArgs.V
	pn.DoCommit(&commitArgs)

	//TODO:need check about the return number
	if prepareReply.Na != prepareArgs.N {
		return true, false, prepareArgs.N
	} else {
		return true, true, prepareArgs.N
	}
}
예제 #2
0
파일: paxos_impl.go 프로젝트: postfix/gobby
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
	}
}
예제 #3
0
파일: paxos_impl.go 프로젝트: postfix/gobby
//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
}