def step(self, *args, **kwargs): flattened_args = signature.flatten_args(self._signature, args, kwargs) actor_id = workflow_context.get_current_workflow_id() if not self.readonly: if self._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 self.readonly: _actor_method = _wrap_readonly_actor_method( actor_id, self._original_class, self._method_name) else: _actor_method = _wrap_actor_method(self._original_class, self._method_name) workflow_data = WorkflowData( func_body=_actor_method, inputs=workflow_inputs, name=self._name, step_options=self._options, user_metadata=self._user_metadata, ) wf = Workflow(workflow_data) return wf
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
def _record_step_status(step_id: "StepID", status: "WorkflowStatus", outputs: List["ObjectRef"] = []) -> None: workflow_id = workflow_context.get_current_workflow_id() workflow_manager = get_management_actor() ray.get( workflow_manager.update_step_status.remote(workflow_id, step_id, status, outputs))
def _workflow_step_executor( func: Callable, context: "WorkflowStepContext", step_id: "StepID", baked_inputs: "_BakedWorkflowInputs", runtime_options: "WorkflowStepRuntimeOptions", ) -> Tuple[Any, Any]: """Executor function for workflow step. Args: step_id: ID of the step. func: The workflow step function. baked_inputs: The processed inputs for the step. context: Workflow step context. Used to access correct storage etc. runtime_options: Parameters for workflow step execution. Returns: Workflow step output. """ # Part 1: update the context for the step workflow_context.update_workflow_step_context(context, step_id) context = workflow_context.get_workflow_step_context() step_type = runtime_options.step_type # Part 2: resolve inputs args, kwargs = baked_inputs.resolve() # Part 3: execute the step store = workflow_storage.get_workflow_storage() try: step_prerun_metadata = {"start_time": time.time()} store.save_step_prerun_metadata(step_id, step_prerun_metadata) persisted_output, volatile_output = _wrap_run(func, runtime_options, *args, **kwargs) step_postrun_metadata = {"end_time": time.time()} store.save_step_postrun_metadata(step_id, step_postrun_metadata) except Exception as e: commit_step(store, step_id, None, exception=e) raise e # Part 4: save outputs 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, exception=None) if isinstance(persisted_output, Workflow): outer_most_step_id = context.outer_most_step_id if step_type == StepType.FUNCTION: # Passing down outer most step so inner nested steps would # access the same outer most step. if not context.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. Pass down "outer_most_step_id". with workflow_context.fork_workflow_step_context( outer_most_step_id=outer_most_step_id): result = execute_workflow(persisted_output) # 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 context.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
def _workflow_step_executor(step_type: StepType, func: Callable, context: workflow_context.WorkflowStepContext, step_id: "StepID", baked_inputs: "_BakedWorkflowInputs", catch_exceptions: bool, max_retries: int) -> 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. 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. 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) 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 = context.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 context.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. Pass down "outer_most_step_id". with workflow_context.fork_workflow_step_context( outer_most_step_id=outer_most_step_id): result = execute_workflow(persisted_output) # 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 context.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
def _workflow_id(self): from ray.workflow.workflow_context import \ get_current_workflow_id return get_current_workflow_id()
def _workflow_step_executor( func: Callable, context: "WorkflowStepContext", step_id: "StepID", baked_inputs: "_BakedWorkflowInputs", runtime_options: "WorkflowStepRuntimeOptions", inplace: bool = False, ) -> Tuple[Any, Any]: """Executor function for workflow step. Args: step_id: ID of the step. func: The workflow step function. baked_inputs: The processed inputs for the step. context: Workflow step context. Used to access correct storage etc. runtime_options: Parameters for workflow step execution. inplace: Execute the workflow inplace. Returns: Workflow step output. """ # Part 1: update the context for the step workflow_context.update_workflow_step_context(context, step_id) context = workflow_context.get_workflow_step_context() step_type = runtime_options.step_type context.checkpoint_context.checkpoint = runtime_options.checkpoint # Part 2: resolve inputs args, kwargs = baked_inputs.resolve() # Part 3: execute the step store = workflow_storage.get_workflow_storage() try: step_prerun_metadata = {"start_time": time.time()} store.save_step_prerun_metadata(step_id, step_prerun_metadata) with workflow_context.workflow_execution(): persisted_output, volatile_output = _wrap_run( func, runtime_options, *args, **kwargs) step_postrun_metadata = {"end_time": time.time()} store.save_step_postrun_metadata(step_id, step_postrun_metadata) except Exception as e: # Always checkpoint the exception. commit_step(store, step_id, None, exception=e) raise e # Part 4: save outputs 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: # TODO(suquark): Validate checkpoint options before # commit the step. store = workflow_storage.get_workflow_storage() if CheckpointMode(runtime_options.checkpoint) == CheckpointMode.SYNC: commit_step( store, step_id, persisted_output, exception=None, ) if isinstance(persisted_output, Workflow): sub_workflow = persisted_output outer_most_step_id = context.outer_most_step_id assert volatile_output is None if step_type == StepType.FUNCTION: # Passing down outer most step so inner nested steps would # access the same outer most step. if not context.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() if inplace: _step_options = sub_workflow.data.step_options if (_step_options.step_type != StepType.WAIT and runtime_options.ray_options != _step_options.ray_options): logger.warning( f"Workflow step '{sub_workflow.step_id}' uses " f"a Ray option different to its caller step '{step_id}' " f"and will be executed inplace. Ray assumes it still " f"consumes the same resource as the caller. This may result " f"in oversubscribing resources.") return ( InplaceReturnedWorkflow( sub_workflow, {"outer_most_step_id": outer_most_step_id}), None, ) # Execute sub-workflow. Pass down "outer_most_step_id". with workflow_context.fork_workflow_step_context( outer_most_step_id=outer_most_step_id): result = execute_workflow(sub_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 context.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