def test_path_last():

    path = Path(['a1', 'a2', 'a3'])
    assert 'a3' == path.last()

    path = Path([])
    assert path.last() is None
Exemple #2
0
    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[:])
Exemple #3
0
    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)
Exemple #4
0
    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
Exemple #5
0
 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)