def _create(self, args: Tuple[Any], kwargs: Dict[str, Any]): workflow_storage = WorkflowStorage(self._actor_id, self._storage) workflow_storage.save_actor_class_body(self._metadata.cls) ref = self._actor_method_call("__init__", args, kwargs) workflow_manager = get_or_create_management_actor() # keep the ref in a list to prevent dereference ray.get(workflow_manager.init_actor.remote(self._actor_id, [ref]))
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 _create(self, args: Tuple[Any], kwargs: Dict[str, Any]): workflow_storage = WorkflowStorage(self._actor_id, self._storage) workflow_storage.save_actor_class_body(self._metadata.cls) # TODO(suquark): This is just a temporary solution. # A virtual actor writer should take place of this solution later. arg_list = self._metadata.flatten_args("__init__", args, kwargs) init_step = _virtual_actor_init.step(self._metadata.cls, arg_list) init_step._step_id = self._metadata.cls.__init__.__name__ ref = init_step.run_async(workflow_id=self._actor_id) workflow_manager = get_or_create_management_actor() # keep the ref in a list to prevent dereference ray.get(workflow_manager.init_actor.remote(self._actor_id, [ref]))
def get_actor(actor_id: str, storage: Storage) -> VirtualActor: """Get an virtual actor. Args: actor_id: The ID of the actor. storage: The storage of the actor. Returns: A virtual actor. """ ws = WorkflowStorage(actor_id, storage) cls = ws.load_actor_class_body() v_cls = VirtualActorClass._from_class(cls) return v_cls._construct(actor_id, storage)
def commit_step(store: workflow_storage.WorkflowStorage, step_id: "StepID", ret: Union[Workflow, Any], outer_most_step_id: Optional[str] = None): """Checkpoint the step output. Args: store: The storage the current workflow is using. step_id: The ID of the step. ret: The returned object of the workflow step. 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. """ if isinstance(ret, Workflow): store.save_subworkflow(ret) store.save_step_output(step_id, ret, outer_most_step_id)
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)
def _construct_resume_workflow_from_step( reader: workflow_storage.WorkflowStorage, step_id: StepID) -> Union[Workflow, StepID]: """Try to construct a workflow (step) that recovers the workflow step. If the workflow step already has an output checkpointing file, we return the workflow step id instead. Args: reader: The storage reader for inspecting the step. step_id: The ID of the step we want to recover. Returns: A workflow that recovers the step, or a ID of a step that contains the output checkpoint file. """ result: workflow_storage.StepInspectResult = reader.inspect_step(step_id) if result.output_object_valid: # we already have the output return step_id if isinstance(result.output_step_id, str): return _construct_resume_workflow_from_step(reader, result.output_step_id) # output does not exists or not valid. try to reconstruct it. if not result.is_recoverable(): raise WorkflowStepNotRecoverableError(step_id) input_workflows = [] instant_workflow_outputs: Dict[int, str] = {} for i, _step_id in enumerate(result.workflows): r = _construct_resume_workflow_from_step(reader, _step_id) if isinstance(r, Workflow): input_workflows.append(r) else: input_workflows.append(None) instant_workflow_outputs[i] = r workflow_refs = list(map(WorkflowRef, result.workflow_refs)) recovery_workflow: Workflow = _recover_workflow_step.options( max_retries=result.max_retries, catch_exceptions=result.catch_exceptions, **result.ray_options).step(result.object_refs, input_workflows, workflow_refs, instant_workflow_outputs) recovery_workflow._step_id = step_id recovery_workflow.data.step_type = result.step_type return recovery_workflow