def test_path_last(): path = Path(['a1', 'a2', 'a3']) assert 'a3' == path.last() path = Path([]) assert path.last() is None
def _send_request(self, budget: float, spent: float, rq_path: Path, paths: PathsTable, visited: List[AgentName], comp_def: ComputationDef, footprint: float, replica_count: int, hosts: List[AgentName]): target_agt = rq_path.last() cost_to_next = self.route(target_agt) budget_to_next = budget - cost_to_next spent_to_next = spent + cost_to_next self.logger.debug( 'sending replica request from %s to %s for %s - %s (' 'budget = %s, cost to next %s)', self.name, target_agt, rq_path, comp_def.name, budget_to_next, cost_to_next) self.post_msg( replication_computation_name(target_agt), UCSReplicateMessage('replicate_request', budget_to_next, spent_to_next, rq_path, paths, visited, comp_def, footprint, replica_count, hosts), MSG_REPLICATION) # All request must be answered, otherwise the replication is stuck. # Keep track of all request sent. self._pending_requests[(target_agt, comp_def.name)] = \ (budget, spent, rq_path, paths.copy(), visited[:], comp_def, footprint, replica_count, hosts[:])
def on_replicate_request(self, budget: float, spent: float, rq_path: Path, paths: PathsTable, visited: List[AgentName], comp_def: ComputationDef, footprint: float, replica_count: int, hosts: List[str]): assert self.agt_name == rq_path.last() if rq_path in paths: paths.pop(rq_path) if self.agt_name not in visited: # first visit for this node visited.append(self.agt_name) self._add_hosting_path(spent, comp_def.name, rq_path, paths) neighbors = self.replication_neighbors() # If some agents have left during replication, some may still be in # the received paths table even though sending replication_request to # them would block the replication. paths = filter_missing_agents_paths(paths, neighbors | {self.agt_name}) # Available & affordable with current remaining budget, paths from here: affordable_paths = affordable_path_from(rq_path, budget + spent, paths) # self.logger.debug('Affordable path %s %s %s %s \n%s', budget, spent, # rq_path, affordable_paths, paths) # Paths to next candidates from paths table. target_paths = (rq_path + Path(p.head()) for _, p in affordable_paths) for target_path in target_paths: forwarded, replica_count = \ self._visit_path(budget, spent, target_path, paths, visited, comp_def, footprint, replica_count, hosts) if forwarded: return self.logger.info('No reachable path for %s with budget %s ', comp_def.name, budget) # Either: # * No path : Not on a already known path # * or all paths are too expensive for our current budget (meaning # that we are at the last node in the known path) # In both cases, we now look at neighbors costs and store them if we # do not already known a cheaper path to them. neighbors_path = ((n, self.route(n), rq_path + Path(n)) for n in neighbors if n not in visited) for n, r, p in neighbors_path: cheapest, cheapest_path = cheapest_path_to(n, paths) if cheapest > spent + r: if cheapest_path in paths: paths.pop(cheapest_path) paths[p] = spent + r else: # self.logger.debug('Cheaper path known to %s : %s (%s)', p, # cheapest_path, cheapest) pass self._send_answer(budget, spent, rq_path, paths, visited, comp_def, footprint, replica_count, hosts)
def _visit_path(self, budget: float, spent: float, target_path: Path, paths: PathsTable, visited: List[AgentName], comp_def: ComputationDef, footprint: float, replica_count: int, hosts: List[str]): """ Visit a path in the replication graph. Visiting can means attempting to host a replica, if we are on a __hosting__ node, or forwarding to another agent or answering the requester. Parameters ---------- budget spent target_path paths visited comp_def footprint replica_count hosts Returns ------- forwarded: boolean a boolean indicating if the request has been answered or forwarded to another agent. replica_count: int the updated replica count. """ if target_path.last() == '__hosting__': # We are actually 'visiting' the '__hosting__' virtual node # so we must remove it form the paths. paths.pop(target_path) if self._can_host(target_path.head(), comp_def.name, footprint): self._accept_replica(target_path.head(), comp_def, footprint) hosts.append(self.agent_def.name) replica_count -= 1 if replica_count == 0: self.logger.info( 'Target resiliency reached for %s, report back to ' 'requester , hosts : %s', comp_def.name, hosts) self._send_answer(budget, spent, target_path[:-1], paths, visited, comp_def, footprint, replica_count, hosts) return True, replica_count return False, replica_count # If the cheapest path was __hosting__, we can still try # to visit the next path (as we known __hosting__ never # have any other neighbor) => consider the request as not forwarded return False, replica_count self._send_request(budget, spent, target_path, paths, visited, comp_def, footprint, replica_count, hosts) return True, replica_count
def _send_answer(self, budget: float, spent: float, rq_path: Path, paths: PathsTable, visited: List[AgentName], comp_def: ComputationDef, footprint: float, replica_count: int, hosts: List[AgentName]): assert rq_path.last() == self.agt_name target_agt = rq_path.before_last() cost_to_target = self.route(target_agt) budget += cost_to_target spent -= cost_to_target self.logger.debug( 'sending replica answer from %s to %s for %s %s' '( %s %s %s )', self.name, target_agt, rq_path, comp_def.name, budget, spent, cost_to_target) self.post_msg( replication_computation_name(target_agt), UCSReplicateMessage('replicate_answer', budget, spent, rq_path, paths, visited, comp_def, footprint, replica_count, hosts), MSG_REPLICATION)