def recv_acceptrequest(self, msg, instance): """Update instance state appropriately based upon msg. (b) 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. """ if msg['prop_num'] >= instance['acceptor_prepare_prop_num']: dbprint("Instance %d: accepting prop %s (current is %s)" % (instance['instance_id'], msg['prop_num'], instance['acceptor_cur_prop_num']), level=4) instance['acceptor_cur_prop_num'] = msg.prop_num instance['acceptor_cur_prop_value'] = msg.prop_value self.writeAll(Msg({ "msg_type": "acceptnotify", "prop_num": msg.prop_num, "prop_value": msg.prop_value, "instance_id": instance['instance_id'] })) else: if config.NACKS_ENABLED: d = { 'msg_type': 'nack_acceptrequest', 'prop_num': msg['prop_num'], 'instance_id': instance['instance_id'], } if config.NACKS_ENABLED == 2: d['prev_prop_num'] = instance['acceptor_cur_prop_num'] d['prev_prop_value'] = instance['acceptor_cur_prop_value'] self.writeMessage(msg['uid'], Msg(d))
def process(self, tx_id, d): """Process an operation that's been passed up through Paxos.""" dbprint("processing op %r, tx id %d" % (d, tx_id), level=4) assert tx_id == self.tx_version+1, "process: tx_id %d != tx_version+1: %d" % (tx_id, self.tx_version+1) self.history.append((tx_id, d)) assert isinstance(d, dict), "process: %s is not a dict" % (d,) if self.lock_holder is not None: if self.lock_holder != d['uid']: dbprint("ignoring op '%s', lock held by %s" % (d, self.lock_holder), level=3) self.tx_version = tx_id return op = d['type'] if op == "nop": pass elif op == "attemptlock": self.process_lock(d) elif op == "unlock": self.process_unlock(d) elif op == "db_op": self.process_op(d) if tx_id in self.waiters: for d in self.waiters[tx_id]: d.callback(tx_id) del self.waiters[tx_id] self.tx_version = tx_id
def process(self, tx_id, d): """Process an operation that's been passed up through Paxos.""" dbprint("processing op %r, tx id %d" % (d, tx_id), level=4) assert tx_id == self.tx_version + 1, "process: tx_id %d != tx_version+1: %d" % ( tx_id, self.tx_version + 1) self.history.append((tx_id, d)) assert isinstance(d, dict), "process: %s is not a dict" % (d, ) if self.lock_holder is not None: if self.lock_holder != d['uid']: dbprint("ignoring op '%s', lock held by %s" % (d, self.lock_holder), level=3) self.tx_version = tx_id return op = d['type'] if op == "nop": pass elif op == "attemptlock": self.process_lock(d) elif op == "unlock": self.process_unlock(d) elif op == "db_op": self.process_op(d) if tx_id in self.waiters: for d in self.waiters[tx_id]: d.callback(tx_id) del self.waiters[tx_id] self.tx_version = tx_id
def recv_acceptrequest(self, msg, instance): """Update instance state appropriately based upon msg. (b) 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. """ if msg['prop_num'] >= instance['acceptor_prepare_prop_num']: dbprint("Instance %d: accepting prop %s (current is %s)" % (instance['instance_id'], msg['prop_num'], instance['acceptor_cur_prop_num']), level=4) instance['acceptor_cur_prop_num'] = msg.prop_num instance['acceptor_cur_prop_value'] = msg.prop_value self.writeAll( Msg({ "msg_type": "acceptnotify", "prop_num": msg.prop_num, "prop_value": msg.prop_value, "instance_id": instance['instance_id'] })) else: if config.NACKS_ENABLED: d = { 'msg_type': 'nack_acceptrequest', 'prop_num': msg['prop_num'], 'instance_id': instance['instance_id'], } if config.NACKS_ENABLED == 2: d['prev_prop_num'] = instance['acceptor_cur_prop_num'] d['prev_prop_value'] = instance['acceptor_cur_prop_value'] self.writeMessage(msg['uid'], Msg(d))
def value_learned(self, instance): assert instance['status'] == "completed" dbprint("passing TX(%s, %s) up" % (instance['instance_id'], instance['value']), level=3) self.manager._passup_tx((instance['instance_id'], instance['value'])) return instance
def writeMessage(self, uid, msg): dbprint("Sent %s message to %s\n%s\n" % (msg['msg_type'], uid, msg), level=1) msg.contents['uid'] = self.uid msg = msg.serialize() addr = self.hosts[uid] self.transport.write(msg, addr)
def recv_acceptnotify(self, msg, instance): """Update instance state appropriately based upon msg. If the proposal has been accepted by a quorum, it's completed. """ # if we've already learnt it's been accepted, there's no need to # deal with it any more if instance['status'] == "completed": return s = instance['learner_accepted'].setdefault(msg['prop_num'], set()) s.add(msg['uid']) if len(s) >= self.quorum_size: dbprint("Instance %d: learnt value %s (prop %s)" % (instance['instance_id'], msg['prop_value'], msg['prop_num']), level=4) instance['status'] = "completed" instance['value'] = msg['prop_value'] assert not instance['callback'].called, "completion error 1: %s: %s" % (msg, instance) instance['callback'].callback(instance)
def datagramReceived(self, msg, host): """Called when a message is received by a specific agent. """ self._msgs.append((msg, host)) try: m = parse_message(msg) # If we haven't heard this host before, add them to the record if m['uid'] not in self.hosts and m['uid'] != self.uid: self.addHost(m['uid'], host) t = m['msg_type'] dbprint("Got %s message from %s\n%s\n" % (m['msg_type'], host, m), level=1) if m['instance_id'] is not None: i = m['instance_id'] if i not in self.instances: if i >= self.current_instance_number: self.current_instance_number = i+1 self.instances[i] = self.create_instance(i) else: assert i < self.current_instance_number, "known but oddly large instance number %s (%s)" % (i, self.current_instance_number) instance = self.instances[i] if instance['status'] == 'completed': dbprint("dropping msg as instance %s is already completed" % i, level=2) return else: instance = None method = getattr(self, "recv_%s" % t) method(m, instance) except (InvalidMessageException, KeyError), e: dbprint("%s received invalid message %s (%s)" % (self, msg, e), level=4) raise
def recv_acceptnotify(self, msg, instance): """Update instance state appropriately based upon msg. If the proposal has been accepted by a quorum, it's completed. """ # if we've already learnt it's been accepted, there's no need to # deal with it any more if instance['status'] == "completed": return s = instance['learner_accepted'].setdefault(msg['prop_num'], set()) s.add(msg['uid']) if len(s) >= self.quorum_size: dbprint( "Instance %d: learnt value %s (prop %s)" % (instance['instance_id'], msg['prop_value'], msg['prop_num']), level=4) instance['status'] = "completed" instance['value'] = msg['prop_value'] assert not instance[ 'callback'].called, "completion error 1: %s: %s" % (msg, instance) instance['callback'].callback(instance)
def chase_up(self, instance_id): dbprint("chasing up instance %s" % instance_id, level=2) if instance_id not in self.instances: dbprint("chase up: haven't heard of %s yet, starting again" % instance_id, level=2) self.instances[i] = self.create_instance(instance_id) self.proposer_start(i, "nop", restart=False) else: dbprint("chase up: already heard of %s, being mean anyway" % instance_id, level=2) i = self.instances[instance_id] self.proposer_start(i, "nop", restart=False)
def recv_promise(self, msg, instance): """Update instance state appropriately based upon msg. (a) 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 if the responses reported no proposals, a value of its own choosing. """ # If this is an old message or we're in the wrong state, ignore if msg['prop_num'] != instance['last_tried']:# or instance['status'] != "trying": dbprint("proposer ignoring msg %s, not appropriate (%s)" % (msg, instance), level=1) return instance['quorum'].add(msg['uid']) if msg['prev_prop_value'] is not None: if msg['prev_prop_num'] > instance['proposer_prev_prop_num']: dbprint("loading old prop value of %s (num %s)" % (msg['prev_prop_value'], msg['prev_prop_num']), level=2) instance['proposer_prev_prop_num'] = msg['prev_prop_num'] instance['proposer_prev_prop_value'] = msg['prev_prop_value'] else: dbprint("ignoring old prop value of %s (num %s) for prop %s" % (msg['prev_prop_value'], msg['prev_prop_num'], instance['proposer_prev_prop_value']), level=2) if len(instance['quorum']) >= self.quorum_size: # If this is the message that tips us over the edge and we # finally accept the proposal, deal with it appropriately. if instance['status'] == 'trying': self.poll(instance, msg['prop_num'])
def recv_promise(self, msg, instance): """Update instance state appropriately based upon msg. (a) 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 if the responses reported no proposals, a value of its own choosing. """ # If this is an old message or we're in the wrong state, ignore if msg['prop_num'] != instance[ 'last_tried']: # or instance['status'] != "trying": dbprint("proposer ignoring msg %s, not appropriate (%s)" % (msg, instance), level=1) return instance['quorum'].add(msg['uid']) if msg['prev_prop_value'] is not None: if msg['prev_prop_num'] > instance['proposer_prev_prop_num']: dbprint("loading old prop value of %s (num %s)" % (msg['prev_prop_value'], msg['prev_prop_num']), level=2) instance['proposer_prev_prop_num'] = msg['prev_prop_num'] instance['proposer_prev_prop_value'] = msg['prev_prop_value'] else: dbprint("ignoring old prop value of %s (num %s) for prop %s" % (msg['prev_prop_value'], msg['prev_prop_num'], instance['proposer_prev_prop_value']), level=2) if len(instance['quorum']) >= self.quorum_size: # If this is the message that tips us over the edge and we # finally accept the proposal, deal with it appropriately. if instance['status'] == 'trying': self.poll(instance, msg['prop_num'])
def datagramReceived(self, msg, host): """Called when a message is received by a specific agent. """ self._msgs.append((msg, host)) try: m = parse_message(msg) # If we haven't heard this host before, add them to the record if m['uid'] not in self.hosts and m['uid'] != self.uid: self.addHost(m['uid'], host) t = m['msg_type'] dbprint("Got %s message from %s\n%s\n" % (m['msg_type'], host, m), level=1) if m['instance_id'] is not None: i = m['instance_id'] if i not in self.instances: if i >= self.current_instance_number: self.current_instance_number = i + 1 self.instances[i] = self.create_instance(i) else: assert i < self.current_instance_number, "known but oddly large instance number %s (%s)" % ( i, self.current_instance_number) instance = self.instances[i] if instance['status'] == 'completed': dbprint( "dropping msg as instance %s is already completed" % i, level=2) return else: instance = None method = getattr(self, "recv_%s" % t) method(m, instance) except (InvalidMessageException, KeyError), e: dbprint("%s received invalid message %s (%s)" % (self, msg, e), level=4) raise
def writeAll(self, msg): dbprint("%s sent message %s to all" % (self, msg), level=1) for uid in self.hosts: self.writeMessage(uid, msg)
def process_unlock(self, d): """Handle someone requesting to take the global lock""" guid = d['uid'] dbprint("unlock: released by %s" % guid, level=3) self.lock_holder = None
def process_op(self, d): try: op = parse_op(d) op.perform_op(self.db) except InvalidOp, e: dbprint("Invalid op (%s)" % e)
def process_lock(self, d): """Handle someone requesting to take the global lock""" guid = d['uid'] dbprint("lock: taken by %s" % guid, level=3) self.lock_holder = guid
def addHost(self, uid, host): dbprint("Adding node %s (%s)" % (uid, host), level=3) self.hosts[uid] = host self.quorum_size = (len(self.hosts)+1) // 2
def addHost(self, uid, host): dbprint("Adding node %s (%s)" % (uid, host), level=3) self.hosts[uid] = host self.quorum_size = (len(self.hosts) + 1) // 2