Implement named actors using the GCS service (#8328)

This commit is contained in:
Edward Oakes
2020-05-09 08:58:10 -05:00
committed by GitHub
parent 93138e617a
commit 2677b71003
37 changed files with 483 additions and 87 deletions
+2 -1
View File
@@ -17,7 +17,7 @@ from ray.includes.common cimport (
CBuffer,
CRayObject
)
from ray.includes.libcoreworker cimport CFiberEvent
from ray.includes.libcoreworker cimport CActorHandle, CFiberEvent
from ray.includes.unique_ids cimport (
CObjectID,
CActorID
@@ -86,6 +86,7 @@ cdef class CoreWorker:
self, worker, outputs, const c_vector[CObjectID] return_ids,
c_vector[shared_ptr[CRayObject]] *returns)
cdef yield_current_fiber(self, CFiberEvent &fiber_event)
cdef make_actor_handle(self, CActorHandle *c_actor_handle)
cdef class FunctionDescriptor:
cdef:
+37 -15
View File
@@ -112,6 +112,12 @@ include "includes/libcoreworker.pxi"
logger = logging.getLogger(__name__)
def gcs_actor_service_enabled():
return (
RayConfig.instance().gcs_service_enabled() and
RayConfig.instance().gcs_actor_service_enabled())
cdef int check_status(const CRayStatus& status) nogil except -1:
if status.ok():
return 0
@@ -125,6 +131,8 @@ cdef int check_status(const CRayStatus& status) nogil except -1:
raise KeyboardInterrupt()
elif status.IsTimedOut():
raise RayTimeoutError(message)
elif status.IsNotFound():
raise ValueError(message)
else:
raise RayletError(message)
@@ -899,6 +907,7 @@ cdef class CoreWorker:
placement_resources,
int32_t max_concurrency,
c_bool is_detached,
c_string name,
c_bool is_asyncio,
c_string extension_data):
cdef:
@@ -922,7 +931,7 @@ cdef class CoreWorker:
CActorCreationOptions(
max_reconstructions, max_concurrency,
c_resources, c_placement_resources,
dynamic_worker_options, is_detached, is_asyncio),
dynamic_worker_options, is_detached, name, is_asyncio),
extension_data,
&c_actor_id))
@@ -1013,23 +1022,12 @@ cdef class CoreWorker:
CCoreWorkerProcess.GetCoreWorker().RemoveActorHandleReference(
c_actor_id)
def deserialize_and_register_actor_handle(self, const c_string &bytes,
ObjectID
outer_object_id):
cdef:
CActorHandle* c_actor_handle
CObjectID c_outer_object_id = (outer_object_id.native() if
outer_object_id else
CObjectID.Nil())
cdef make_actor_handle(self, CActorHandle *c_actor_handle):
worker = ray.worker.global_worker
worker.check_connected()
manager = worker.function_actor_manager
c_actor_id = (CCoreWorkerProcess.GetCoreWorker()
.DeserializeAndRegisterActorHandle(
bytes, c_outer_object_id))
check_status(CCoreWorkerProcess.GetCoreWorker().GetActorHandle(
c_actor_id, &c_actor_handle))
actor_id = ActorID(c_actor_id.Binary())
actor_id = ActorID(c_actor_handle.GetActorID().Binary())
job_id = JobID(c_actor_handle.CreationJobID().Binary())
language = Language.from_native(c_actor_handle.ActorLanguage())
actor_creation_function_descriptor = \
@@ -1064,6 +1062,30 @@ cdef class CoreWorker:
actor_creation_function_descriptor,
worker.current_session_and_job)
def deserialize_and_register_actor_handle(self, const c_string &bytes,
ObjectID
outer_object_id):
cdef:
CActorHandle* c_actor_handle
CObjectID c_outer_object_id = (outer_object_id.native() if
outer_object_id else
CObjectID.Nil())
c_actor_id = (CCoreWorkerProcess.GetCoreWorker()
.DeserializeAndRegisterActorHandle(
bytes, c_outer_object_id))
check_status(CCoreWorkerProcess.GetCoreWorker().GetActorHandle(
c_actor_id, &c_actor_handle))
return self.make_actor_handle(c_actor_handle)
def get_named_actor_handle(self, const c_string &name):
cdef:
CActorHandle* c_actor_handle
with nogil:
check_status(CCoreWorkerProcess.GetCoreWorker()
.GetNamedActorHandle(name, &c_actor_handle))
return self.make_actor_handle(c_actor_handle)
def serialize_actor_handle(self, ActorID actor_id):
cdef:
c_string output
+16 -3
View File
@@ -10,7 +10,7 @@ import ray._raylet
import ray.signature as signature
import ray.worker
from ray import ActorClassID, Language
from ray._raylet import PythonFunctionDescriptor
from ray._raylet import PythonFunctionDescriptor, gcs_actor_service_enabled
from ray import cross_language
logger = logging.getLogger(__name__)
@@ -472,11 +472,23 @@ class ActorClass:
"Please use Actor._remote(name='some_name') "
"to associate the name.")
if name and not detached:
raise ValueError("Only detached actors can be named. "
"Please use Actor._remote(detached=True, "
"name='some_name').")
if name == "":
raise ValueError("Actor name cannot be an empty string.")
# Check whether the name is already taken.
# TODO(edoakes): this check has a race condition because two drivers
# could pass the check and then create the same named actor. We should
# instead check this when we create the actor, but that's currently an
# async call.
if name is not None:
try:
ray.util.get_actor(name)
except ValueError: # name is not taken, expected.
except ValueError: # Name is not taken.
pass
else:
raise ValueError(
@@ -551,6 +563,7 @@ class ActorClass:
actor_placement_resources,
max_concurrency,
detached,
name if name is not None else "",
is_asyncio,
# Store actor_method_cpu in actor handle's extension data.
extension_data=str(actor_method_cpu))
@@ -566,7 +579,7 @@ class ActorClass:
worker.current_session_and_job,
original_handle=True)
if name is not None:
if name is not None and not gcs_actor_service_enabled():
ray.util.register_actor(name, actor_handle)
return actor_handle
+5 -1
View File
@@ -88,6 +88,9 @@ cdef extern from "ray/common/status.h" namespace "ray" nogil:
@staticmethod
CRayStatus UnexpectedSystemExit()
@staticmethod
CRayStatus NotFound()
c_bool ok()
c_bool IsOutOfMemory()
c_bool IsKeyError()
@@ -101,6 +104,7 @@ cdef extern from "ray/common/status.h" namespace "ray" nogil:
c_bool IsTimedOut()
c_bool IsInterrupted()
c_bool IsSystemExit()
c_bool IsNotFound()
c_string ToString()
c_string CodeAsString()
@@ -231,7 +235,7 @@ cdef extern from "ray/core_worker/common.h" nogil:
const unordered_map[c_string, double] &resources,
const unordered_map[c_string, double] &placement_resources,
const c_vector[c_string] &dynamic_worker_options,
c_bool is_detached, c_bool is_asyncio)
c_bool is_detached, c_string &name, c_bool is_asyncio)
cdef extern from "ray/gcs/gcs_client.h" nogil:
cdef cppclass CGcsClientOptions "ray::gcs::GcsClientOptions":
+2
View File
@@ -123,6 +123,8 @@ cdef extern from "ray/core_worker/core_worker.h" nogil:
CObjectID *c_actor_handle_id)
CRayStatus GetActorHandle(const CActorID &actor_id,
CActorHandle **actor_handle) const
CRayStatus GetNamedActorHandle(const c_string &name,
CActorHandle **actor_handle)
void AddLocalReference(const CObjectID &object_id)
void RemoveLocalReference(const CObjectID &object_id)
void PromoteObjectToPlasma(const CObjectID &object_id)
+5
View File
@@ -1,3 +1,4 @@
from libcpp cimport bool as c_bool
from libc.stdint cimport int64_t, uint64_t, uint32_t
from libcpp.string cimport string as c_string
from libcpp.unordered_map cimport unordered_map
@@ -83,3 +84,7 @@ cdef extern from "ray/common/ray_config.h" nogil:
uint32_t maximum_gcs_deletion_batch_size() const
int64_t max_direct_call_object_size() const
c_bool gcs_service_enabled() const
c_bool gcs_actor_service_enabled() const
+17 -6
View File
@@ -658,20 +658,31 @@ def test_detached_actor(ray_start_regular):
def ping(self):
return "pong"
with pytest.raises(Exception, match="Detached actors must be named"):
with pytest.raises(
ValueError, match="Actor name cannot be an empty string"):
DetachedActor._remote(detached=True, name="")
with pytest.raises(ValueError, match="Detached actors must be named"):
DetachedActor._remote(detached=True)
with pytest.raises(ValueError, match="Please use a different name"):
_ = DetachedActor._remote(name="d_actor")
with pytest.raises(ValueError, match="Only detached actors can be named"):
DetachedActor._remote(name="d_actor")
DetachedActor._remote(detached=True, name="d_actor")
with pytest.raises(ValueError, match="Please use a different name"):
DetachedActor._remote(detached=True, name="d_actor")
redis_address = ray_start_regular["redis_address"]
actor_name = "DetachedActor"
get_actor_name = "d_actor"
create_actor_name = "DetachedActor"
driver_script = """
import ray
ray.init(address="{}")
existing_actor = ray.util.get_actor("{}")
assert ray.get(existing_actor.ping.remote()) == "pong"
@ray.remote
class DetachedActor:
def ping(self):
@@ -679,10 +690,10 @@ class DetachedActor:
actor = DetachedActor._remote(name="{}", detached=True)
ray.get(actor.ping.remote())
""".format(redis_address, actor_name)
""".format(redis_address, get_actor_name, create_actor_name)
run_string_as_driver(driver_script)
detached_actor = ray.util.get_actor(actor_name)
detached_actor = ray.util.get_actor(create_actor_name)
assert ray.get(detached_actor.ping.remote()) == "pong"
+11 -5
View File
@@ -26,11 +26,17 @@ def get_actor(name):
Returns:
The ActorHandle object corresponding to the name.
"""
actor_name = _calculate_key(name)
pickled_state = _internal_kv_get(actor_name)
if pickled_state is None:
raise ValueError("The actor with name={} doesn't exist".format(name))
handle = pickle.loads(pickled_state)
if ray._raylet.gcs_actor_service_enabled():
worker = ray.worker.global_worker
handle = worker.core_worker.get_named_actor_handle(name)
else:
actor_name = _calculate_key(name)
pickled_state = _internal_kv_get(actor_name)
if pickled_state is None:
raise ValueError(
"The actor with name={} doesn't exist".format(name))
handle = pickle.loads(pickled_state)
return handle