def __init__(self, my_node, querier, bootstrap_lookup_f, bootstrap_nodes): self.my_node = my_node self.querier = querier self.bootstrap_lookup = bootstrap_lookup_f #Copy the bootstrap list self.bootstrap_nodes = [n for n in bootstrap_nodes] self.table = RoutingTable(my_node, NODES_PER_BUCKET) self.ping_msg = message.OutgoingPingQuery(my_node.id) self.find_closest_msg = message.OutgoingFindNodeQuery( my_node.id, my_node.id) #This must be called by an external party: self.do_bootstrap() #After initializing callbacks # maintenance variables # self.last_maintenance_index = -1 self.maintenance_mode = BOOTSTRAP_MODE self.lookup_mode = False self._query_received_queue = _QueryReceivedQueue(self.table) self._found_nodes_queue = _FoundNodesQueue(self.table) self.maintenance_tasks = [ self._ping_a_staled_rnode, self._ping_a_query_received_node, self._ping_a_found_node ]
def __init__(self, my_node, querier, bootstrap_lookup_f, bootstrap_nodes): self.my_node = my_node self.querier = querier self.bootstrap_lookup = bootstrap_lookup_f #Copy the bootstrap list self.bootstrap_nodes = [n for n in bootstrap_nodes] self.table = RoutingTable(my_node, NODES_PER_BUCKET) self.ping_msg = message.OutgoingPingQuery(my_node.id) self.find_closest_msg = message.OutgoingFindNodeQuery( my_node.id, my_node.id) #This must be called by an external party: self.do_bootstrap() #After initializing callbacks # maintenance variables # self.last_maintenance_index = -1 self.maintenance_mode = BOOTSTRAP_MODE self.lookup_mode = False self._query_received_queue = _QueryReceivedQueue(self.table) self._found_nodes_queue = _FoundNodesQueue(self.table) self.maintenance_tasks = [self._ping_a_staled_rnode, self._ping_a_query_received_node, self._ping_a_found_node]
class RoutingManager(object): def __init__(self, my_node, querier, bootstrap_lookup_f, bootstrap_nodes): self.my_node = my_node self.querier = querier self.bootstrap_lookup = bootstrap_lookup_f #Copy the bootstrap list self.bootstrap_nodes = [n for n in bootstrap_nodes] self.table = RoutingTable(my_node, NODES_PER_BUCKET) self.ping_msg = message.OutgoingPingQuery(my_node.id) self.find_closest_msg = message.OutgoingFindNodeQuery( my_node.id, my_node.id) #This must be called by an external party: self.do_bootstrap() #After initializing callbacks # maintenance variables # self.last_maintenance_index = -1 self.maintenance_mode = BOOTSTRAP_MODE self.lookup_mode = False self._query_received_queue = _QueryReceivedQueue(self.table) self._found_nodes_queue = _FoundNodesQueue(self.table) self.maintenance_tasks = [ self._ping_a_staled_rnode, self._ping_a_query_received_node, self._ping_a_found_node ] def notify_lookup_start(self): self.lookup_mode = True def notify_lookup_stop(self): self.lookup_mode = False def do_maintenance(self): if self.lookup_mode: # Do not send maintenance pings during lookup return MAINTENANCE_DELAY[self.maintenance_mode] if self.maintenance_mode == BOOTSTRAP_MODE: self._do_bootstrap() # elif self.maintenance_mode == FIND_CLOSEST_MODE: # self.bootstrap_lookup() # self.maintenance_mode = NORMAL_MODE else: for _ in range(len(self.maintenance_tasks)): task = self.maintenance_tasks.pop(0) self.maintenance_tasks.append(task) if task(): break return MAINTENANCE_DELAY[self.maintenance_mode] def _do_bootstrap(self): m_bucket, _ = self.table.buckets[0] if not self.bootstrap_nodes\ or not m_bucket.there_is_room(): # Stop bootstrap. Go normal self.maintenance_mode = NORMAL_MODE return index = random.randint(0, len(self.bootstrap_nodes) - 1) self._send_maintenance_ping(self.bootstrap_nodes[index]) del self.bootstrap_nodes[index] def _ping_a_staled_rnode(self): m_bucket, r_bucket = self.table.buckets[0] rnode = m_bucket.get_stalest_rnode() if time.time() > rnode.last_seen + QUARANTINE_PERIOD: print '[%f] staled rnode %d' % (time.time(), self.my_node.log_distance(rnode)) self._send_maintenance_ping(rnode) return True return False def _ping_a_found_node(self): node_ = self._found_nodes_queue.pop(0) if node_: logger.debug('pinging node found: %r', node_) print '[%f] found node %d' % (time.time(), self.my_node.log_distance(node_)) self._send_maintenance_ping(node_) return True return False def _ping_a_query_received_node(self): node_ = self._query_received_queue.pop(0) if node_: print '[%f] query received %d' % (time.time(), self.my_node.log_distance(node_)) self._send_maintenance_ping(node_) return True return False def _send_maintenance_ping(self, node_): m_bucket, r_bucket = self.table.buckets[0] if m_bucket.there_is_room() and r_bucket.there_is_room(): target = identifier.RandomId() msg = message.OutgoingFindNodeQuery(self.my_node.id, target) m_bucket, r_bucket = self.table.buckets[0] print 'FIND_NODE(%d:%d:%d)' % (0, len(m_bucket), len(r_bucket)), else: print 'PING', msg = self.ping_msg if node_.id: m_bucket, r_bucket = self.table.buckets[0] print 'to %r - %d:%d:%d --' % (node_.addr, node_.log_distance(self.my_node), len(m_bucket), len(r_bucket)) else: print 'to UNKNOWN id' self.querier.send_query(msg, node_) def on_query_received(self, node_): pass def on_response_received(self, node_, rtt): #TODO2:, rtt=0): logger.debug('on response received %d', rtt) m_bucket, r_bucket = self.table.buckets[0] rnode = m_bucket.get_rnode(node_) if rnode: # node in routing table self._update_rnode_on_response_received(rnode, rtt) return # The node is not in main rnode = r_bucket.get_rnode(node_) if rnode: # node in replacement table # let's see whether there is room in the main self._update_rnode_on_response_received(rnode, rtt) #TODO: leave this for the maintenance task if m_bucket.there_is_room(node_): m_bucket.add(rnode) self._update_rnode_on_response_received(rnode, rtt) r_bucket.remove(rnode) return # The node is nowhere # Add to main table (if the bucket is not full) #TODO: check wether in replacement_mode if m_bucket.there_is_room(): rnode = node_.get_rnode() m_bucket.add(rnode) self._update_rnode_on_response_received(rnode, rtt) return # The main bucket is full # Let's see whether this node's latency is good highest_rtt_rnode = m_bucket.get_highest_rtt_rnode(10) if rtt < highest_rtt_rnode.rtt: # Replace high rtt node print 'replacing rnode (better RTT) %d' % (node_.log_distance( self.my_node)) m_bucket.remove(highest_rtt_rnode) rnode = node_.get_rnode() m_bucket.add(rnode) self._update_rnode_on_response_received(rnode, rtt) return worst_rnode = self._worst_rnode(r_bucket.rnodes) # Get the worst node in replacement bucket and see whether # it's bad enough to be replaced by node_ if worst_rnode \ and worst_rnode.timeouts_in_a_row() > MAX_NUM_TIMEOUTS: # This node is better candidate than worst_rnode r_bucket.remove(worst_rnode) rnode = node_.get_rnode() r_bucket.add(rnode) self._update_rnode_on_response_received(rnode, rtt) def on_error_received(self, node_): pass def on_timeout(self, node_): if not node_.id: return # This is a bootstrap node (just addr, no id) m_bucket, r_bucket = self.table.buckets[0] rnode = m_bucket.get_rnode(node_) if rnode: # node in routing table: check whether it should be removed self._update_rnode_on_timeout(rnode) print 'refresh repl_bucket %d-%d' % (node_.log_distance( self.my_node), len(m_bucket.rnodes)) r_rnodes_rtt_sorted = sorted( r_bucket.rnodes, lambda x, y: int(1000 * (x.rtt - y.rtt))) for r_rnode in r_rnodes_rtt_sorted: self._query_received_queue.add(r_rnode) m_bucket.remove(rnode) if r_bucket.there_is_room(): r_bucket.add(rnode) else: worst_rnode = self._worst_rnode(r_bucket.rnodes) if worst_rnode: # Replace worst node in replacement table r_bucket.remove(worst_rnode) #self._refresh_replacement_bucket(replacement_bucket) # We don't want to ping the node which just did timeout r_bucket.add(rnode) # Node is not in main table rnode = r_bucket.get_rnode(node_) if rnode: # Node in replacement table: just update rnode self._update_rnode_on_timeout(rnode) def on_nodes_found(self, nodes): logger.debug('nodes found: %r', nodes) self._found_nodes_queue.add(nodes) def get_closest_rnodes(self, target_id, num_nodes=DEFAULT_NUM_NODES): return self.table.get_closest_rnodes(target_id, num_nodes) def get_main_rnodes(self): return self.table.get_main_rnodes() def print_stats(self): self.table.print_stats() def _update_rnode_on_query_received(self, rnode): """Register a query from node. You should call this method when receiving a query from this node. """ current_time = time.time() rnode.last_action_ts = time.time() rnode.msgs_since_timeout += 1 rnode.num_queries += 1 rnode.last_events.append((current_time, node.QUERY)) rnode.last_events[:rnode.max_last_events] rnode.last_seen = current_time def _update_rnode_on_response_received(self, rnode, rtt): """Register a reply from rnode. You should call this method when receiving a response from this rnode. """ rnode.rtt = rtt current_time = time.time() #rnode._reset_refresh_task() if rnode.in_quarantine: rnode.in_quarantine = \ rnode.last_action_ts < current_time - node.QUARANTINE_PERIOD rnode.last_action_ts = current_time rnode.num_responses += 1 rnode.last_events.append((time.time(), node.RESPONSE)) rnode.last_events[:rnode.max_last_events] rnode.last_seen = current_time def _update_rnode_on_timeout(self, rnode): """Register a timeout for this rnode. You should call this method when getting a timeout for this node. """ rnode.last_action_ts = time.time() rnode.msgs_since_timeout = 0 rnode.num_timeouts += 1 rnode.last_events.append((time.time(), node.TIMEOUT)) rnode.last_events[:rnode.max_last_events] def _worst_rnode(self, rnodes): max_num_timeouts = -1 worst_rnode_so_far = None for rnode in rnodes: num_timeouots = rnode.timeouts_in_a_row() if num_timeouots >= max_num_timeouts: max_num_timeouts = num_timeouots worst_rnode_so_far = rnode return worst_rnode_so_far
class RoutingManager(object): def __init__(self, my_node, querier, bootstrap_lookup_f, bootstrap_nodes): self.my_node = my_node self.querier = querier self.bootstrap_lookup = bootstrap_lookup_f #Copy the bootstrap list self.bootstrap_nodes = [n for n in bootstrap_nodes] self.table = RoutingTable(my_node, NODES_PER_BUCKET) self.ping_msg = message.OutgoingPingQuery(my_node.id) self.find_closest_msg = message.OutgoingFindNodeQuery( my_node.id, my_node.id) #This must be called by an external party: self.do_bootstrap() #After initializing callbacks # maintenance variables # self.last_maintenance_index = -1 self.maintenance_mode = BOOTSTRAP_MODE self.lookup_mode = False self._query_received_queue = _QueryReceivedQueue(self.table) self._found_nodes_queue = _FoundNodesQueue(self.table) self.maintenance_tasks = [self._ping_a_staled_rnode, self._ping_a_query_received_node, self._ping_a_found_node] def notify_lookup_start(self): self.lookup_mode = True def notify_lookup_stop(self): self.lookup_mode = False def do_maintenance(self): if self.lookup_mode: # Do not send maintenance pings during lookup return MAINTENANCE_DELAY[self.maintenance_mode] if self.maintenance_mode == BOOTSTRAP_MODE: self._do_bootstrap() # elif self.maintenance_mode == FIND_CLOSEST_MODE: # self.bootstrap_lookup() # self.maintenance_mode = NORMAL_MODE else: for _ in range(len(self.maintenance_tasks)): task = self.maintenance_tasks.pop(0) self.maintenance_tasks.append(task) if task(): break return MAINTENANCE_DELAY[self.maintenance_mode] def _do_bootstrap(self): m_bucket, _ = self.table.buckets[0] if not self.bootstrap_nodes\ or not m_bucket.there_is_room(): # Stop bootstrap. Go normal self.maintenance_mode = NORMAL_MODE return index = random.randint(0, len(self.bootstrap_nodes) - 1) self._send_maintenance_ping(self.bootstrap_nodes[index]) del self.bootstrap_nodes[index] def _ping_a_staled_rnode(self): m_bucket, r_bucket = self.table.buckets[0] rnode = m_bucket.get_stalest_rnode() if time.time() > rnode.last_seen + QUARANTINE_PERIOD: print '[%f] staled rnode %d' % ( time.time(), self.my_node.log_distance(rnode)) self._send_maintenance_ping(rnode) return True return False def _ping_a_found_node(self): node_ = self._found_nodes_queue.pop(0) if node_: logger.debug('pinging node found: %r', node_) print '[%f] found node %d' % ( time.time(), self.my_node.log_distance(node_)) self._send_maintenance_ping(node_) return True return False def _ping_a_query_received_node(self): node_ = self._query_received_queue.pop(0) if node_: print '[%f] query received %d' % ( time.time(), self.my_node.log_distance(node_)) self._send_maintenance_ping(node_) return True return False def _send_maintenance_ping(self, node_): m_bucket, r_bucket = self.table.buckets[0] if m_bucket.there_is_room() and r_bucket.there_is_room(): target = identifier.RandomId() msg = message.OutgoingFindNodeQuery(self.my_node.id, target) m_bucket, r_bucket = self.table.buckets[0] print 'FIND_NODE(%d:%d:%d)' % (0, len(m_bucket), len(r_bucket)), else: print 'PING', msg = self.ping_msg if node_.id: m_bucket, r_bucket = self.table.buckets[0] print 'to %r - %d:%d:%d --' % ( node_.addr, node_.log_distance(self.my_node), len(m_bucket), len(r_bucket)) else: print 'to UNKNOWN id' self.querier.send_query(msg, node_) def on_query_received(self, node_): pass def on_response_received(self, node_, rtt): #TODO2:, rtt=0): logger.debug('on response received %d', rtt) m_bucket, r_bucket = self.table.buckets[0] rnode = m_bucket.get_rnode(node_) if rnode: # node in routing table self._update_rnode_on_response_received(rnode, rtt) return # The node is not in main rnode = r_bucket.get_rnode(node_) if rnode: # node in replacement table # let's see whether there is room in the main self._update_rnode_on_response_received(rnode, rtt) #TODO: leave this for the maintenance task if m_bucket.there_is_room(node_): m_bucket.add(rnode) self._update_rnode_on_response_received(rnode, rtt) r_bucket.remove(rnode) return # The node is nowhere # Add to main table (if the bucket is not full) #TODO: check wether in replacement_mode if m_bucket.there_is_room(): rnode = node_.get_rnode() m_bucket.add(rnode) self._update_rnode_on_response_received(rnode, rtt) return # The main bucket is full # Let's see whether this node's latency is good highest_rtt_rnode = m_bucket.get_highest_rtt_rnode(10) if rtt < highest_rtt_rnode.rtt: # Replace high rtt node print 'replacing rnode (better RTT) %d' % ( node_.log_distance(self.my_node)) m_bucket.remove(highest_rtt_rnode) rnode = node_.get_rnode() m_bucket.add(rnode) self._update_rnode_on_response_received(rnode, rtt) return worst_rnode = self._worst_rnode(r_bucket.rnodes) # Get the worst node in replacement bucket and see whether # it's bad enough to be replaced by node_ if worst_rnode \ and worst_rnode.timeouts_in_a_row() > MAX_NUM_TIMEOUTS: # This node is better candidate than worst_rnode r_bucket.remove(worst_rnode) rnode = node_.get_rnode() r_bucket.add(rnode) self._update_rnode_on_response_received(rnode, rtt) def on_error_received(self, node_): pass def on_timeout(self, node_): if not node_.id: return # This is a bootstrap node (just addr, no id) m_bucket, r_bucket = self.table.buckets[0] rnode = m_bucket.get_rnode(node_) if rnode: # node in routing table: check whether it should be removed self._update_rnode_on_timeout(rnode) print 'refresh repl_bucket %d-%d' % ( node_.log_distance(self.my_node), len(m_bucket.rnodes)) r_rnodes_rtt_sorted = sorted( r_bucket.rnodes, lambda x,y: int(1000*(x.rtt-y.rtt))) for r_rnode in r_rnodes_rtt_sorted: self._query_received_queue.add(r_rnode) m_bucket.remove(rnode) if r_bucket.there_is_room(): r_bucket.add(rnode) else: worst_rnode = self._worst_rnode(r_bucket.rnodes) if worst_rnode: # Replace worst node in replacement table r_bucket.remove(worst_rnode) #self._refresh_replacement_bucket(replacement_bucket) # We don't want to ping the node which just did timeout r_bucket.add(rnode) # Node is not in main table rnode = r_bucket.get_rnode(node_) if rnode: # Node in replacement table: just update rnode self._update_rnode_on_timeout(rnode) def on_nodes_found(self, nodes): logger.debug('nodes found: %r', nodes) self._found_nodes_queue.add(nodes) def get_closest_rnodes(self, target_id, num_nodes=DEFAULT_NUM_NODES): return self.table.get_closest_rnodes(target_id, num_nodes) def get_main_rnodes(self): return self.table.get_main_rnodes() def print_stats(self): self.table.print_stats() def _update_rnode_on_query_received(self, rnode): """Register a query from node. You should call this method when receiving a query from this node. """ current_time = time.time() rnode.last_action_ts = time.time() rnode.msgs_since_timeout += 1 rnode.num_queries += 1 rnode.last_events.append((current_time, node.QUERY)) rnode.last_events[:rnode.max_last_events] rnode.last_seen = current_time def _update_rnode_on_response_received(self, rnode, rtt): """Register a reply from rnode. You should call this method when receiving a response from this rnode. """ rnode.rtt = rtt current_time = time.time() #rnode._reset_refresh_task() if rnode.in_quarantine: rnode.in_quarantine = \ rnode.last_action_ts < current_time - node.QUARANTINE_PERIOD rnode.last_action_ts = current_time rnode.num_responses += 1 rnode.last_events.append((time.time(), node.RESPONSE)) rnode.last_events[:rnode.max_last_events] rnode.last_seen = current_time def _update_rnode_on_timeout(self, rnode): """Register a timeout for this rnode. You should call this method when getting a timeout for this node. """ rnode.last_action_ts = time.time() rnode.msgs_since_timeout = 0 rnode.num_timeouts += 1 rnode.last_events.append((time.time(), node.TIMEOUT)) rnode.last_events[:rnode.max_last_events] def _worst_rnode(self, rnodes): max_num_timeouts = -1 worst_rnode_so_far = None for rnode in rnodes: num_timeouots = rnode.timeouts_in_a_row() if num_timeouots >= max_num_timeouts: max_num_timeouts = num_timeouots worst_rnode_so_far = rnode return worst_rnode_so_far