def _actor_method_call(self, method_name: str, args,
                        kwargs) -> "ObjectRef":
     flattened_args = self._metadata.flatten_args(method_name, args, kwargs)
     cls = self._metadata.cls
     method = getattr(cls, method_name, None)
     if method is None:
         raise AttributeError(f"Method '{method_name}' does not exist.")
     workflow_inputs = serialization_context.make_workflow_inputs(
         flattened_args)
     readonly = getattr(method, "__virtual_actor_readonly__", False)
     with workflow_context.workflow_step_context(self._actor_id,
                                                 self._storage.storage_url):
         if readonly:
             _readonly_actor_method = _wrap_readonly_actor_method(
                 self._actor_id, cls, method_name)
             # TODO(suquark): Support actor options.
             workflow_data = WorkflowData(
                 func_body=_readonly_actor_method,
                 inputs=workflow_inputs,
                 max_retries=1,
                 catch_exceptions=False,
                 ray_options={},
             )
             wf = Workflow(workflow_data)
             return execute_virtual_actor_step(wf.id, workflow_data, True)
         else:
             raise NotImplementedError(
                 "Virtual actor writer mode has not been supported yet.")
Example #2
0
            def step(method_name, method, *args, **kwargs):
                readonly = getattr(method, "__virtual_actor_readonly__", False)
                flattened_args = self.flatten_args(method_name, args, kwargs)
                actor_id = workflow_context.get_current_workflow_id()
                if not readonly:
                    if method_name == "__init__":
                        state_ref = None
                    else:
                        ws = WorkflowStorage(actor_id, get_global_storage())
                        state_ref = WorkflowRef(ws.get_entrypoint_step_id())
                    # This is a hack to insert a positional argument.
                    flattened_args = [signature.DUMMY_TYPE, state_ref
                                      ] + flattened_args
                workflow_inputs = serialization_context.make_workflow_inputs(
                    flattened_args)

                if readonly:
                    _actor_method = _wrap_readonly_actor_method(
                        actor_id, self.cls, method_name)
                    step_type = StepType.READONLY_ACTOR_METHOD
                else:
                    _actor_method = _wrap_actor_method(self.cls, method_name)
                    step_type = StepType.ACTOR_METHOD
                # TODO(suquark): Support actor options.
                workflow_data = WorkflowData(
                    func_body=_actor_method,
                    step_type=step_type,
                    inputs=workflow_inputs,
                    max_retries=1,
                    catch_exceptions=False,
                    ray_options={},
                    name=None,
                )
                wf = Workflow(workflow_data)
                return wf
Example #3
0
 def _build_workflow(*args, **kwargs) -> Workflow:
     # validate if the input arguments match the signature of the
     # original function.
     reconstructed_signature = inspect.Signature(
         parameters=self._func_signature)
     try:
         reconstructed_signature.bind(*args, **kwargs)
     except TypeError as exc:  # capture a friendlier stacktrace
         raise TypeError(str(exc)) from None
     workflows: List[Workflow] = []
     object_refs: List[ObjectRef] = []
     with serialization_context.workflow_args_serialization_context(
             workflows, object_refs):
         # NOTE: When calling 'ray.put', we trigger python object
         # serialization. Under our serialization context,
         # Workflows and ObjectRefs are separated from the arguments,
         # leaving a placeholder object with all other python objects.
         # Then we put the placeholder object to object store,
         # so it won't be mutated later. This guarantees correct
         # semantics. See "tests/test_variable_mutable.py" as
         # an example.
         input_placeholder: ObjectRef = ray.put((args, kwargs))
         if object_refs:
             raise ValueError(
                 "There are ObjectRefs in workflow inputs. However "
                 "workflow currently does not support checkpointing "
                 "ObjectRefs.")
     return Workflow(self._func, self._run_step, input_placeholder,
                     workflows, object_refs, self._step_max_retries,
                     self._catch_exceptions, self._ray_options)
Example #4
0
    def _actor_method_call(self, method_name: str, args,
                           kwargs) -> "ObjectRef":
        cls = self._metadata.cls
        method = getattr(cls, method_name, None)
        if method is None:
            raise AttributeError(f"Method '{method_name}' does not exist.")
        readonly = getattr(method, "__virtual_actor_readonly__", False)

        flattened_args = self._metadata.flatten_args(method_name, args, kwargs)
        if not readonly:
            if method_name == "__init__":
                state_ref = None
            else:
                ws = WorkflowStorage(self._actor_id, self._storage)
                state_ref = WorkflowRef(ws.get_entrypoint_step_id())
            # This is a hack to insert a positional argument.
            flattened_args = [signature.DUMMY_TYPE, state_ref] + flattened_args
        workflow_inputs = serialization_context.make_workflow_inputs(
            flattened_args)

        if readonly:
            _actor_method = _wrap_readonly_actor_method(
                self._actor_id, cls, method_name)
            step_type = StepType.READONLY_ACTOR_METHOD
        else:
            _actor_method = _wrap_actor_method(cls, method_name)
            step_type = StepType.ACTOR_METHOD
        # TODO(suquark): Support actor options.
        workflow_data = WorkflowData(
            func_body=_actor_method,
            step_type=step_type,
            inputs=workflow_inputs,
            max_retries=1,
            catch_exceptions=False,
            ray_options={},
        )
        wf = Workflow(workflow_data)
        with workflow_context.workflow_step_context(self._actor_id,
                                                    self._storage.storage_url):
            if readonly:
                return execute_workflow(wf).volatile_output
            else:
                return wf.run_async(self._actor_id)
Example #5
0
    def save_subworkflow(self, workflow: Workflow) -> None:
        """Save the DAG and inputs of the sub-workflow.

        Args:
            workflow: A sub-workflow. Could be a nested workflow inside
                a workflow step.
        """
        assert not workflow.executed
        for w in workflow.iter_workflows_in_dag():
            self._write_step_inputs(w.id, w.get_inputs())
Example #6
0
    def save_subworkflow(self, workflow: Workflow) -> None:
        """Save the DAG and inputs of the sub-workflow.

        Args:
            workflow: A sub-workflow. Could be a nested workflow inside
                a workflow step.
        """
        assert not workflow.executed
        tasks = [
            self._write_step_inputs(w.id, w.data)
            for w in workflow.iter_workflows_in_dag()
        ]
        asyncio_run(asyncio.gather(*tasks))
Example #7
0
 def _build_workflow(*args, **kwargs) -> Workflow:
     flattened_args = signature.flatten_args(self._func_signature, args,
                                             kwargs)
     workflow_inputs = serialization_context.make_workflow_inputs(
         flattened_args)
     workflow_data = WorkflowData(
         func_body=self._func,
         inputs=workflow_inputs,
         max_retries=self._max_retries,
         catch_exceptions=self._catch_exceptions,
         ray_options=self._ray_options,
     )
     return Workflow(workflow_data)
Example #8
0
        def _build_workflow(*args, **kwargs) -> Workflow:
            flattened_args = signature.flatten_args(self._func_signature, args,
                                                    kwargs)

            def prepare_inputs():
                ensure_ray_initialized()
                return serialization_context.make_workflow_inputs(
                    flattened_args)

            workflow_data = WorkflowData(
                func_body=self._func,
                step_type=StepType.FUNCTION,
                inputs=None,
                max_retries=self._max_retries,
                catch_exceptions=self._catch_exceptions,
                ray_options=self._ray_options,
                name=self._name,
            )
            return Workflow(workflow_data, prepare_inputs)
Example #9
0
def execute_workflow(
        workflow: Workflow,
        outer_most_step_id: Optional[str] = None) -> ray.ObjectRef:
    """Execute workflow.

    To fully explain what we are doing, we need to introduce some syntax first.
    The syntax for dependencies between workflow steps
    "A.step(B.step())" is "A - B"; the syntax for nested workflow steps
    "def A(): return B.step()" is "A / B".

    In a chain/DAG of step dependencies, the "output step" is the step of last
    (topological) order. For example, in "A - B - C", C is the output step.

    In a chain of nested workflow steps, the initial "output step" is
    called the "outer most step" for other "output steps". For example, in
    "A / B / C / D", "A" is the outer most step for "B", "C", "D";
    in the hybrid workflow "((A - B) / C / D) - (E / (F - G) / H)",
    "B" is the outer most step for "C", "D"; "E" is the outer most step
    for "G", "H".

    Args:
        workflow: The workflow to be executed.
        outer_most_step_id: The ID of the outer most workflow. None if it
            does not exists. See "step_executor.execute_workflow" for detailed
            explanation.
    Returns:
        An object ref that represent the result.
    """
    if outer_most_step_id is None:
        # The current workflow step returns a nested workflow, and
        # there is no outer step for the current step. So the current
        # step is the outer most step for the inner nested workflow
        # steps.
        outer_most_step_id = workflow_context.get_current_step_id()
    # Passing down outer most step so inner nested steps would
    # access the same outer most step.
    return workflow.execute(outer_most_step_id)