def _(self, message, channel, create_reply=Response.create): """Learn Phase. To learn that a value has been chosen, a learner must find out that a proposal has been accepted by a majority of acceptors. """ LOG.debug("RECEIVED message {0}".format(message)) self.accepted_proposals.setdefault(message.proposal, set()) \ .add(message.sender) minimum_quorum = len(channel.replicas) // 2 + 1 accepted_quorum = len(self.accepted_proposals.get(message.proposal)) if accepted_quorum == minimum_quorum: # Here we reset for the next round. self.state.write(Role.PROPOSED, self.state.read(Role.PROPOSED) \ .next()) self.state.write(Role.VALUE, None) channel.unicast(Request.create(receiver=message.receiver, sender=message.receiver)) self.notification.send(create_reply(proposal=message.proposal)) if accepted_quorum >= minimum_quorum: self._ledger.append(LedgerEntry(number=message.proposal.number, value=message.value)) reply = create_reply(proposal=message.proposal) channel.unicast(reply) if accepted_quorum == len(channel.replicas): del self.accepted_proposals[message.proposal]
def _(self, message, channel, create_reply=Accept.create): """Accept Phase. If the proposer receives a response to its prepare requests (numbered n) from a majority of acceptors, then it sends an accept request to each of those acceptors for a proposal numbered n with a value v, where v is the value of the highest-numbered proposal among the responses, or is any value if the responses reported no proposals. """ LOG.debug("RECEIVED message {0}".format(message)) self.received_promises.setdefault(message.proposal, set()) \ .add(message.sender) if (self.highest_proposal is None or message.accepted_proposal >= self.highest_proposal): self.highest_proposal = message.proposal if message.value: self.selector.set(message.proposal, message.value) value = self.selector.get(message.proposal) minimum_quorum = len(channel.replicas) // 2 + 1 received_promises = len(self.received_promises.get(message.proposal)) if received_promises >= minimum_quorum: reply = create_reply(sender=message.receiver, proposal=message.proposal, value=value) channel.broadcast(reply)
def _(self, message, channel): """Handle request from out-of-sync replica""" LOG.debug("RECEIVED message {0}".format(message)) proposals = self._ledger.get_range(message.proposal)[:Learner.SYNC_SIZE] is_finished = proposals[-1] == self.state.read(Role.ACCEPTED) channel.unicast(Synced.create(receiver=message.sender, sender=message.receiver, proposal=proposals, finished=is_finished))
def send(self, message): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) coro = loop.create_connection(lambda: RemoteProtocol(message, loop), self.ip, self.port) try: loop.run_until_complete(coro) except ConnectionRefusedError as e: LOG.debug("I/O send error({0}: {1}".format(message.receiver, e.strerror)) pass loop.close()
def _(self, message, channel, create_reply=Prepare.create): """Prepare Phase. A proposer selects a proposal number n and sends a prepare request with number n to a majority of acceptors. """ LOG.debug("RECEIVED message {0}".format(message)) if message.value: self.selector.add(message.value) if not self.selector.is_empty(): current_proposal = self.state.read(Role.PROPOSED) reply = create_reply(sender=message.receiver, proposal=current_proposal) channel.broadcast(reply) self.notification.wait(current_proposal)
def _(self, message, channel): """Handle response for updating this replica""" LOG.debug("RECEIVED message {0}".format(message)) synced_proposals = message.proposal self._ledger.extend(synced_proposals) current_proposal = Proposal("sync", synced_proposals[-1].number) next_proposal = Proposal("sync", synced_proposals[-1].number + 1) self.state.write(Role.PROPOSED, current_proposal) self.state.write(Role.PROMISED, current_proposal) self.state.write(Role.ACCEPTED, current_proposal) if not message.finished: channel.unicast(Sync.create( receiver=message.sender, sender=message.receiver, proposal=self.state.read(Role.ACCEPTED))) else: self.state.write(Role.PROPOSED, next_proposal) self.resume()
def _(self, message, channel, create_reply=Promise.create): """Promise Phase. If an acceptor receives a prepare request with number n greater than that of any prepare request to which it has already responded then it responds to the request with a promise not to accept any more proposals numbered less than n and with the highest-numbered proposal (if any) that it has accepted. """ LOG.debug("RECEIVED message {0}".format(message)) if message.proposal >= self.state.read(Role.PROMISED): self.state.write(Role.PROMISED, message.proposal) reply = create_reply( sender=message.receiver, receiver=message.sender, proposal=message.proposal, accepted_proposal=self.state.read(Role.ACCEPTED), value=self.state.read(Role.VALUE)) channel.unicast(reply) else: reply = Nack.create(sender=message.receiver, receiver=message.sender) channel.unicast(reply)
def _(self, message, channel, create_reply=Accepted.create): """Accepted Phase. If an acceptor receives an accept request for a proposal numbered n, it accepts the proposal unless it has already responded to a prepare request having a number greater than n. """ LOG.debug("RECEIVED message {0}".format(message)) if message.proposal >= self.state.read(Role.PROMISED): if message.proposal > self.state.read(Role.ACCEPTED): self.state.write(Role.ACCEPTED, message.proposal) self.state.write(Role.VALUE, message.value) reply = create_reply(sender=message.receiver, value=self.state.read(Role.VALUE), proposal=message.proposal) channel.broadcast(reply) else: reply = Nack.create(sender=message.receiver, receiver=message.sender, value=self.state.read(Role.VALUE), proposal=message.proposal) channel.unicast(reply)