def _workflow_step_executor(func: Callable, context: workflow_context.WorkflowStepContext, step_id: StepID, step_inputs: StepInputTupleToResolve, outer_most_step_id: StepID) -> Any: """Executor function for workflow step. Args: func: The workflow step function. context: Workflow step context. Used to access correct storage etc. step_id: The ID of the step. step_inputs: The inputs tuple of the step. outer_most_step_id: See "step_executor.execute_workflow" for explanation. Returns: Workflow step output. """ # Before running the actual function, we # 1. Setup the workflow context, so we have proper access to # workflow storage. # 2. Decode step inputs to arguments and keyword-arguments. workflow_context.update_workflow_step_context(context, step_id) args, kwargs = _resolve_step_inputs(step_inputs) # Running the actual step function ret = func(*args, **kwargs) # Save workflow output commit_step(ret, outer_most_step_id) if isinstance(ret, Workflow): # execute sub-workflow return execute_workflow(ret, outer_most_step_id) return ret
def _workflow_step_executor(func: Callable, context: workflow_context.WorkflowStepContext, step_id: "StepID", step_inputs: "StepInputTupleToResolve", outer_most_step_id: "StepID", catch_exceptions: bool, step_max_retries: int) -> Any: """Executor function for workflow step. Args: func: The workflow step function. context: Workflow step context. Used to access correct storage etc. step_id: The ID of the step. step_inputs: The inputs tuple of the step. outer_most_step_id: See "step_executor.execute_workflow" for explanation. catch_exceptions: If set to be true, return (Optional[Result], Optional[Error]) instead of Result. step_max_retries: Max number of retries encounter of a failure. Returns: Workflow step output. """ ret = None err = None # step_max_retries are for application level failure. # For ray failure, we should use max_retries. from ray.experimental.workflow.common import WorkflowStatus from ray.experimental.workflow.common import Workflow for _ in range(step_max_retries): try: workflow_context.update_workflow_step_context(context, step_id) args, kwargs = _resolve_step_inputs(step_inputs) # Running the actual step function ret = func(*args, **kwargs) # Save workflow output store = workflow_storage.get_workflow_storage() commit_step(store, step_id, ret, outer_most_step_id) if isinstance(ret, Workflow): # execute sub-workflow ret = execute_workflow(ret, outer_most_step_id) err = None break except BaseException as e: err = e if catch_exceptions: _record_step_status(step_id, WorkflowStatus.FINISHED) return (ret, err) else: if err is not None: _record_step_status(step_id, WorkflowStatus.RESUMABLE) raise err _record_step_status(step_id, WorkflowStatus.FINISHED) return ret
def _workflow_step_executor(step_type: StepType, func: Callable, context: workflow_context.WorkflowStepContext, step_id: "StepID", step_inputs: "StepInputTupleToResolve", outer_most_step_id: "StepID", catch_exceptions: bool, max_retries: int, last_step_of_workflow: bool) -> Any: """Executor function for workflow step. Args: step_type: The type of workflow step. func: The workflow step function. context: Workflow step context. Used to access correct storage etc. step_id: The ID of the step. step_inputs: The inputs tuple of the step. outer_most_step_id: See "step_executor.execute_workflow" for explanation. catch_exceptions: If set to be true, return (Optional[Result], Optional[Error]) instead of Result. max_retries: Max number of retries encounter of a failure. last_step_of_workflow: The step that generates the output of the workflow (including nested steps). Returns: Workflow step output. """ workflow_context.update_workflow_step_context(context, step_id) args, kwargs = _resolve_step_inputs(step_inputs) state, output = _wrap_run(func, step_type, step_id, catch_exceptions, max_retries, *args, **kwargs) if step_type != StepType.READONLY_ACTOR_METHOD: store = workflow_storage.get_workflow_storage() # Save workflow output commit_step(store, step_id, state, outer_most_step_id) # We MUST execute the workflow after saving the output. if isinstance(state, Workflow): if step_type == StepType.FUNCTION: # execute sub-workflow state = execute_workflow(state, outer_most_step_id, last_step_of_workflow) else: # TODO(suquark): Support returning a workflow inside # a virtual actor. raise TypeError("Only a workflow step function " "can return a workflow.") elif last_step_of_workflow: # advance the progress of the workflow store.advance_progress(step_id) _record_step_status(step_id, WorkflowStatus.SUCCESSFUL) logger.info(get_step_status_info(WorkflowStatus.SUCCESSFUL)) return state, output
def _workflow_step_executor(step_type: StepType, func: Callable, context: workflow_context.WorkflowStepContext, step_id: "StepID", baked_inputs: "_BakedWorkflowInputs", outer_most_step_id: "StepID", catch_exceptions: bool, max_retries: int, last_step_of_workflow: bool) -> Any: """Executor function for workflow step. Args: step_type: The type of workflow step. func: The workflow step function. context: Workflow step context. Used to access correct storage etc. step_id: The ID of the step. baked_inputs: The processed inputs for the step. outer_most_step_id: See "step_executor.execute_workflow" for explanation. catch_exceptions: If set to be true, return (Optional[Result], Optional[Error]) instead of Result. max_retries: Max number of retries encounter of a failure. last_step_of_workflow: The step that generates the output of the workflow (including nested steps). Returns: Workflow step output. """ workflow_context.update_workflow_step_context(context, step_id) args, kwargs = _resolve_step_inputs(baked_inputs) persisted_output, volatile_output = _wrap_run(func, step_type, step_id, catch_exceptions, max_retries, *args, **kwargs) if step_type != StepType.READONLY_ACTOR_METHOD: store = workflow_storage.get_workflow_storage() # Save workflow output commit_step(store, step_id, persisted_output, outer_most_step_id) # We MUST execute the workflow after saving the output. if isinstance(persisted_output, Workflow): if step_type == StepType.FUNCTION: # Passing down outer most step so inner nested steps would # access the same outer most step. if not outer_most_step_id: # 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() # execute sub-workflow persisted_output = execute_workflow( persisted_output, outer_most_step_id, last_step_of_workflow).persisted_output else: # TODO(suquark): Support returning a workflow inside # a virtual actor. raise TypeError("Only a workflow step function " "can return a workflow.") elif last_step_of_workflow: # advance the progress of the workflow store.advance_progress(step_id) _record_step_status(step_id, WorkflowStatus.SUCCESSFUL) logger.info(get_step_status_info(WorkflowStatus.SUCCESSFUL)) return persisted_output, volatile_output
def _workflow_step_executor( step_type: StepType, func: Callable, context: workflow_context.WorkflowStepContext, step_id: "StepID", baked_inputs: "_BakedWorkflowInputs", outer_most_step_id: "StepID", catch_exceptions: bool, max_retries: int, last_step_of_workflow: bool) -> Any: """Executor function for workflow step. Args: step_type: The type of workflow step. func: The workflow step function. context: Workflow step context. Used to access correct storage etc. step_id: The ID of the step. baked_inputs: The processed inputs for the step. outer_most_step_id: See "step_executor.execute_workflow" for explanation. catch_exceptions: If set to be true, return (Optional[Result], Optional[Error]) instead of Result. max_retries: Max number of retries encounter of a failure. last_step_of_workflow: The step that generates the output of the workflow (including nested steps). Returns: Workflow step output. """ workflow_context.update_workflow_step_context(context, step_id) args, kwargs = _resolve_step_inputs(baked_inputs) store = workflow_storage.get_workflow_storage() try: persisted_output, volatile_output = _wrap_run( func, step_type, step_id, catch_exceptions, max_retries, *args, **kwargs) except Exception as e: commit_step(store, step_id, None, e, outer_most_step_id) raise e if step_type == StepType.READONLY_ACTOR_METHOD: if isinstance(volatile_output, Workflow): raise TypeError( "Returning a Workflow from a readonly virtual actor " "is not allowed.") assert not isinstance(persisted_output, Workflow) else: store = workflow_storage.get_workflow_storage() commit_step(store, step_id, persisted_output, None, outer_most_step_id) if isinstance(persisted_output, Workflow): if step_type == StepType.FUNCTION: # Passing down outer most step so inner nested steps would # access the same outer most step. if not outer_most_step_id: # 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() assert volatile_output is None # execute sub-workflow result = execute_workflow(persisted_output, outer_most_step_id, last_step_of_workflow) # When virtual actor returns a workflow in the method, # the volatile_output and persisted_output will be put together persisted_output = result.persisted_output volatile_output = result.volatile_output elif last_step_of_workflow: # advance the progress of the workflow store.advance_progress(step_id) _record_step_status(step_id, WorkflowStatus.SUCCESSFUL) logger.info(get_step_status_info(WorkflowStatus.SUCCESSFUL)) if isinstance(volatile_output, Workflow): # This is the case where a step method is called in the virtual actor. # We need to run the method to get the final result. assert step_type == StepType.ACTOR_METHOD volatile_output = volatile_output.run_async( workflow_context.get_current_workflow_id()) return persisted_output, volatile_output