From 4e5a51f76f64b4a5c394e5e435e3137f6e2a455c Mon Sep 17 00:00:00 2001 From: rickyyx Date: Tue, 21 Mar 2023 22:02:38 +0000 Subject: [PATCH 1/6] init Signed-off-by: rickyyx --- python/ray/_raylet.pyx | 7 ++ python/ray/experimental/state/common.py | 2 + python/ray/includes/libcoreworker.pxd | 1 + python/ray/tests/test_state_api_2.py | 85 ++++++++++++++++++- src/ray/core_worker/core_worker.cc | 5 ++ src/ray/core_worker/core_worker.h | 10 +++ .../transport/direct_actor_transport.cc | 11 ++- .../transport/direct_actor_transport.h | 9 ++ src/ray/gcs/gcs_server/gcs_actor_manager.cc | 1 + src/ray/protobuf/core_worker.proto | 4 + src/ray/protobuf/gcs.proto | 3 + 11 files changed, 136 insertions(+), 2 deletions(-) diff --git a/python/ray/_raylet.pyx b/python/ray/_raylet.pyx index e931cb6845621..92c65069d7a6b 100644 --- a/python/ray/_raylet.pyx +++ b/python/ray/_raylet.pyx @@ -928,6 +928,10 @@ cdef void execute_task( print(actor_magic_token, end="") print(actor_magic_token, file=sys.stderr, end="") + # Sets the actor repr name for the actor so other components + # like GCS has such info. + core_worker.set_actor_repr_name(repr(actor)) + if (returns[0].size() > 0 and not inspect.isgenerator(outputs) and len(outputs) != int(returns[0].size())): @@ -1620,6 +1624,9 @@ cdef class CoreWorker: def set_actor_title(self, title): CCoreWorkerProcess.GetCoreWorker().SetActorTitle(title) + def set_actor_repr_name(self, repr_name): + CCoreWorkerProcess.GetCoreWorker().SetActorReprName(repr_name) + def get_plasma_event_handler(self): return self.plasma_event_handler diff --git a/python/ray/experimental/state/common.py b/python/ray/experimental/state/common.py index c6fc8a8eaf0a4..8659604d6a18a 100644 --- a/python/ray/experimental/state/common.py +++ b/python/ray/experimental/state/common.py @@ -380,6 +380,8 @@ class ActorState(StateSchema): is_detached: bool = state_column(filterable=False, detail=True) #: The placement group id that's associated with this actor. placement_group_id: str = state_column(detail=True, filterable=True) + #: Actor's repr name if a customized __repr__ method exists, else empty string. + repr_name: str = state_column(detail=True, filterable=True) @dataclass(init=True) diff --git a/python/ray/includes/libcoreworker.pxd b/python/ray/includes/libcoreworker.pxd index 18db418a28ca2..ee8c34e1ec955 100644 --- a/python/ray/includes/libcoreworker.pxd +++ b/python/ray/includes/libcoreworker.pxd @@ -158,6 +158,7 @@ cdef extern from "ray/core_worker/core_worker.h" nogil: c_bool ShouldCaptureChildTasksInPlacementGroup() const CActorID &GetActorId() void SetActorTitle(const c_string &title) + void SetActorReprName(const c_string &repr_name) void SetWebuiDisplay(const c_string &key, const c_string &message) CTaskID GetCallerId() const ResourceMappingType &GetResourceIDs() const diff --git a/python/ray/tests/test_state_api_2.py b/python/ray/tests/test_state_api_2.py index 7ecdfb7d0102f..9cf126870fae6 100644 --- a/python/ray/tests/test_state_api_2.py +++ b/python/ray/tests/test_state_api_2.py @@ -9,7 +9,13 @@ import pytest from ray._private.profiling import chrome_tracing_dump -from ray.experimental.state.api import list_tasks, list_actors, list_workers, list_nodes +from ray.experimental.state.api import ( + get_actor, + list_tasks, + list_actors, + list_workers, + list_nodes, +) from ray._private.test_utils import wait_for_condition @@ -169,6 +175,83 @@ def verify(): wait_for_condition(verify, timeout=10) +def test_actor_repr_name(shutdown_only): + def _verify_repr_name(id, name): + actor = get_actor(id=id) + assert actor is not None + assert actor["repr_name"] == name + return True + + # Assert simple actor repr name + @ray.remote + class ReprActor: + def __init__(self, x) -> None: + self.x = x + + def __repr__(self) -> str: + return self.x + + def ready(self): + pass + + a = ReprActor.remote(x="repr-name-a") + b = ReprActor.remote(x="repr-name-b") + + wait_for_condition(_verify_repr_name, id=a._actor_id.hex(), name="repr-name-a") + wait_for_condition(_verify_repr_name, id=b._actor_id.hex(), name="repr-name-b") + + # Assert when no __repr__ defined. repr_name should be empty + @ray.remote + class Actor: + pass + + a = Actor.remote() + wait_for_condition(_verify_repr_name, id=a._actor_id.hex(), name="") + + # Assert special actors (async actor, threaded actor, detached actor, named actor) + @ray.remote + class AsyncActor: + def __init__(self, x) -> None: + self.x = x + + def __repr__(self) -> str: + return self.x + + async def ready(self): + pass + + a = AsyncActor.remote(x="async-x") + wait_for_condition(_verify_repr_name, id=a._actor_id.hex(), name="async-x") + + a = ReprActor.options(max_concurrency=3).remote(x="x") + wait_for_condition(_verify_repr_name, id=a._actor_id.hex(), name="x") + + a = ReprActor.options(name="named-actor").remote(x="repr-name") + wait_for_condition(_verify_repr_name, id=a._actor_id.hex(), name="repr-name") + + a = ReprActor.options(name="detached-actor", lifetime="detached").remote( + x="repr-name" + ) + wait_for_condition(_verify_repr_name, id=a._actor_id.hex(), name="repr-name") + ray.kill(a) + + # Assert nested actor class. + class OutClass: + @ray.remote + class InnerActor: + def __init__(self, name) -> None: + self.name = name + + def __repr__(self) -> str: + return self.name + + def get_actor(self, name): + return OutClass.InnerActor.remote(name=name) + + a = OutClass().get_actor(name="inner") + wait_for_condition(_verify_repr_name, id=a._actor_id.hex(), name="inner") + + if __name__ == "__main__": import sys diff --git a/src/ray/core_worker/core_worker.cc b/src/ray/core_worker/core_worker.cc index da47c3722d639..48676a587be90 100644 --- a/src/ray/core_worker/core_worker.cc +++ b/src/ray/core_worker/core_worker.cc @@ -3745,6 +3745,11 @@ void CoreWorker::SetActorTitle(const std::string &title) { actor_title_ = title; } +void CoreWorker::SetActorReprName(const std::string &repr_name) { + RAY_CHECK(direct_task_receiver_ != nullptr); + direct_task_receiver_->SetActorReprName(repr_name); +} + rpc::JobConfig CoreWorker::GetJobConfig() const { return worker_context_.GetCurrentJobConfig(); } diff --git a/src/ray/core_worker/core_worker.h b/src/ray/core_worker/core_worker.h index 08052a2a72a5d..3c3c0c0501e76 100644 --- a/src/ray/core_worker/core_worker.h +++ b/src/ray/core_worker/core_worker.h @@ -374,6 +374,16 @@ class CoreWorker : public rpc::CoreWorkerServiceHandler { void SetActorTitle(const std::string &title); + /// Sets the actor's repr name. + /// + /// This is set explicitly rather than included as part of actor creation task spec + /// because it's only available after running the creation task as it might depend on + /// fields to be be initialized during actor creation task. The repr name will be + /// included as part of actor creation task reply (PushTaskReply) to GCS. + /// + /// \param repr_name Actor repr name. + void SetActorReprName(const std::string &repr_name); + void SetCallerCreationTimestamp(); /// Increase the reference count for this object ID. diff --git a/src/ray/core_worker/transport/direct_actor_transport.cc b/src/ray/core_worker/transport/direct_actor_transport.cc index 5a588a679f698..bf97267363b6c 100644 --- a/src/ray/core_worker/transport/direct_actor_transport.cc +++ b/src/ray/core_worker/transport/direct_actor_transport.cc @@ -187,8 +187,13 @@ void CoreWorkerDirectTaskReceiver::HandleTask( << ", actor_id: " << task_spec.ActorCreationId() << ", status: " << status; } else { + // Set the actor repr name if it's customized by the actor. + if (!actor_repr_name_.empty()) { + reply->set_actor_repr_name(actor_repr_name_); + } RAY_LOG(INFO) << "Actor creation task finished, task_id: " << task_spec.TaskId() - << ", actor_id: " << task_spec.ActorCreationId(); + << ", actor_id: " << task_spec.ActorCreationId() + << ", actor_repr_name: " << actor_repr_name_; } } } @@ -311,5 +316,9 @@ void CoreWorkerDirectTaskReceiver::Stop() { } } +void CoreWorkerDirectTaskReceiver::SetActorReprName(const std::string &repr_name) { + actor_repr_name_ = repr_name; +} + } // namespace core } // namespace ray diff --git a/src/ray/core_worker/transport/direct_actor_transport.h b/src/ray/core_worker/transport/direct_actor_transport.h index 89181af3251dd..38f2ad16153aa 100644 --- a/src/ray/core_worker/transport/direct_actor_transport.h +++ b/src/ray/core_worker/transport/direct_actor_transport.h @@ -94,6 +94,12 @@ class CoreWorkerDirectTaskReceiver { void Stop(); + /// Set the actor repr name for an actor. + /// + /// The actor repr name is only available after actor creation task has been run since + /// the repr name could include data only initialized during the creation task. + void SetActorReprName(const std::string &repr_name); + private: /// Set up the configs for an actor. /// This should be called once for the actor creation task. @@ -135,6 +141,9 @@ class CoreWorkerDirectTaskReceiver { /// Whether this actor executes tasks out of order with respect to client submission /// order. bool execute_out_of_order_ = false; + /// The repr name of the actor instance for an anonymous actor. + /// This is only available after the actor creation task. + std::string actor_repr_name_ = ""; }; } // namespace core diff --git a/src/ray/gcs/gcs_server/gcs_actor_manager.cc b/src/ray/gcs/gcs_server/gcs_actor_manager.cc index c18ae602ec5fd..1e72b4188af1b 100644 --- a/src/ray/gcs/gcs_server/gcs_actor_manager.cc +++ b/src/ray/gcs/gcs_server/gcs_actor_manager.cc @@ -1224,6 +1224,7 @@ void GcsActorManager::OnActorCreationSuccess(const std::shared_ptr &ac auto worker_id = actor->GetWorkerID(); auto node_id = actor->GetNodeID(); mutable_actor_table_data->set_node_id(node_id.Binary()); + mutable_actor_table_data->set_repr_name(reply.actor_repr_name()); RAY_CHECK(!worker_id.IsNil()); RAY_CHECK(!node_id.IsNil()); RAY_CHECK(created_actors_[node_id].emplace(worker_id, actor_id).second); diff --git a/src/ray/protobuf/core_worker.proto b/src/ray/protobuf/core_worker.proto index 2500f896b6a90..6b425b2a6c8e4 100644 --- a/src/ray/protobuf/core_worker.proto +++ b/src/ray/protobuf/core_worker.proto @@ -137,6 +137,10 @@ message PushTaskReply { bool is_application_error = 6; // Whether the task was cancelled before it started running (i.e. while queued). bool was_cancelled_before_running = 7; + // If the task was an actor creation task, and the actor class has a customized + // repr defined for the anonymous actor (not a named actor), the repr name of the + // actor will be piggybacked to GCS to be included as part of ActorTableData. + optional string actor_repr_name = 8; } message DirectActorCallArgWaitCompleteRequest { diff --git a/src/ray/protobuf/gcs.proto b/src/ray/protobuf/gcs.proto index 3e4c4cce84f68..f25106e06886b 100644 --- a/src/ray/protobuf/gcs.proto +++ b/src/ray/protobuf/gcs.proto @@ -155,6 +155,9 @@ message ActorTableData { optional bytes node_id = 29; // Placement group ID if the actor requires a placement group. optional bytes placement_group_id = 30; + // The repr name of the actor if specified with a customized repr method, e.g. __repr__ + // Default to empty string if no customized repr is defined. + string repr_name = 31; } message ErrorTableData { From 4759c5c2616472d5051cd2b3764d337c7d67fe47 Mon Sep 17 00:00:00 2001 From: rickyyx Date: Wed, 22 Mar 2023 00:45:42 +0000 Subject: [PATCH 2/6] comments Signed-off-by: rickyyx --- python/ray/_raylet.pyx | 5 +++-- src/ray/protobuf/gcs.proto | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/python/ray/_raylet.pyx b/python/ray/_raylet.pyx index 92c65069d7a6b..fc11b0bb091c1 100644 --- a/python/ray/_raylet.pyx +++ b/python/ray/_raylet.pyx @@ -922,15 +922,16 @@ cdef void execute_task( if (hasattr(actor_class, "__ray_actor_class__") and (actor_class.__ray_actor_class__.__repr__ != object.__repr__)): + actor_repr = repr(actor) actor_magic_token = "{}{}\n".format( - ray_constants.LOG_PREFIX_ACTOR_NAME, repr(actor)) + ray_constants.LOG_PREFIX_ACTOR_NAME, actor_repr) # Flush on both stdout and stderr. print(actor_magic_token, end="") print(actor_magic_token, file=sys.stderr, end="") # Sets the actor repr name for the actor so other components # like GCS has such info. - core_worker.set_actor_repr_name(repr(actor)) + core_worker.set_actor_repr_name(actor_repr) if (returns[0].size() > 0 and not inspect.isgenerator(outputs) and diff --git a/src/ray/protobuf/gcs.proto b/src/ray/protobuf/gcs.proto index f25106e06886b..a0f75f72c932a 100644 --- a/src/ray/protobuf/gcs.proto +++ b/src/ray/protobuf/gcs.proto @@ -156,6 +156,8 @@ message ActorTableData { // Placement group ID if the actor requires a placement group. optional bytes placement_group_id = 30; // The repr name of the actor if specified with a customized repr method, e.g. __repr__ + // This field is only available after the actor creation task has been run since it + // might depend on actor fields to be initialized in __init__. // Default to empty string if no customized repr is defined. string repr_name = 31; } From 045bc958ac709aaff67c0ff4541aec979b0b59d7 Mon Sep 17 00:00:00 2001 From: rickyyx Date: Thu, 23 Mar 2023 02:22:23 +0000 Subject: [PATCH 3/6] update Signed-off-by: rickyyx --- .../client/src/components/ActorTable.tsx | 23 ++++++++++++++++++ .../client/src/pages/actor/ActorDetail.tsx | 8 +++++++ dashboard/client/src/type/actor.ts | 1 + dashboard/modules/actor/actor_head.py | 2 ++ dashboard/modules/actor/tests/test_actor.py | 4 ++++ dashboard/state_aggregator.py | 13 +++++++++- python/ray/experimental/state/common.py | 24 ++++++++++++++++--- src/ray/gcs/gcs_server/gcs_actor_manager.h | 1 + 8 files changed, 72 insertions(+), 4 deletions(-) diff --git a/dashboard/client/src/components/ActorTable.tsx b/dashboard/client/src/components/ActorTable.tsx index 7e31e6d1375a5..53f03f6f5d45c 100644 --- a/dashboard/client/src/components/ActorTable.tsx +++ b/dashboard/client/src/components/ActorTable.tsx @@ -93,6 +93,25 @@ const ActorTable = ({ ), }, + { + label: "Repr", + helpInfo: ( + + The repr name of the actor instance defined by __repr__. For example, + this actor will have repr "Actor1" +
+
+ @ray.remote +
+ class Actor: +
+  def __repr__(self): +
+   return "Actor1" +
+
+ ), + }, { label: "State", helpInfo: ( @@ -306,6 +325,7 @@ const ActorTable = ({ ({ actorId, actorClass, + reprName, jobId, placementGroupId, pid, @@ -359,6 +379,9 @@ const ActorTable = ({ {actorClass} {name ? name : "-"} + + {reprName ? reprName : "-"} + diff --git a/dashboard/client/src/pages/actor/ActorDetail.tsx b/dashboard/client/src/pages/actor/ActorDetail.tsx index 44c09feb2612e..2e41542a3d16f 100644 --- a/dashboard/client/src/pages/actor/ActorDetail.tsx +++ b/dashboard/client/src/pages/actor/ActorDetail.tsx @@ -92,6 +92,14 @@ const ActorDetailPage = () => { } : { value: "-" }, }, + { + label: "Repr", + content: actorDetail.actorClass + ? { + value: actorDetail.reprName, + } + : { value: "-" }, + }, { label: "Job ID", content: actorDetail.jobId diff --git a/dashboard/client/src/type/actor.ts b/dashboard/client/src/type/actor.ts index bf0bff94c2197..7ac5274307eea 100644 --- a/dashboard/client/src/type/actor.ts +++ b/dashboard/client/src/type/actor.ts @@ -30,6 +30,7 @@ export type Actor = { [key: string]: number; }; exitDetail: string; + reprName: string; }; export type ActorDetail = { diff --git a/dashboard/modules/actor/actor_head.py b/dashboard/modules/actor/actor_head.py index 1a5e5a260e258..45b764217bedd 100644 --- a/dashboard/modules/actor/actor_head.py +++ b/dashboard/modules/actor/actor_head.py @@ -57,6 +57,7 @@ def actor_table_data_to_dict(message): "className", "startTime", "endTime", + "reprName", } light_message = {k: v for (k, v) in orig_message.items() if k in fields} light_message["actorClass"] = orig_message["className"] @@ -141,6 +142,7 @@ async def _update_actors(self): "exitDetail", "startTime", "endTime", + "reprName", ) def process_actor_data_from_pubsub(actor_id, actor_table_data): diff --git a/dashboard/modules/actor/tests/test_actor.py b/dashboard/modules/actor/tests/test_actor.py index 92d09a976c8a2..2d85790a6f8d8 100644 --- a/dashboard/modules/actor/tests/test_actor.py +++ b/dashboard/modules/actor/tests/test_actor.py @@ -33,6 +33,9 @@ def get_pid(self): return os.getpid() + def __repr__(self) -> str: + return "Foo1" + @ray.remote(num_cpus=0, resources={"infeasible_actor": 1}) class InfeasibleActor: pass @@ -76,6 +79,7 @@ class InfeasibleActor: assert actor_response["requiredResources"] == {} assert actor_response["endTime"] == 0 assert actor_response["exitDetail"] == "-" + assert actor_response["reprName"] == "Foo1" for a in actors.values(): # "exitDetail always exits from the response" assert "exitDetail" in a diff --git a/dashboard/state_aggregator.py b/dashboard/state_aggregator.py index 56a633b8c01f5..cb597e5494b5f 100644 --- a/dashboard/state_aggregator.py +++ b/dashboard/state_aggregator.py @@ -709,10 +709,21 @@ async def summarize_tasks(self, option: SummaryApiOptions) -> SummaryApiResponse detail=summary_by == "lineage", ) ) + if summary_by == "func_name": summary_results = TaskSummaries.to_summary_by_func_name(tasks=result.result) else: - summary_results = TaskSummaries.to_summary_by_lineage(tasks=result.result) + # We will need the actors info for actor tasks. + actors = await self.list_actors( + option=ListApiOptions( + timeout=option.timeout, + limit=RAY_MAX_LIMIT_FROM_API_SERVER, + detail=True, + ) + ) + summary_results = TaskSummaries.to_summary_by_lineage( + tasks=result.result, actors=actors.result + ) summary = StateSummary(node_id_to_summary={"cluster": summary_results}) warnings = result.warnings if ( diff --git a/python/ray/experimental/state/common.py b/python/ray/experimental/state/common.py index 8659604d6a18a..a82bef0cf3e20 100644 --- a/python/ray/experimental/state/common.py +++ b/python/ray/experimental/state/common.py @@ -837,7 +837,9 @@ def to_summary_by_func_name(cls, *, tasks: List[Dict]) -> "TaskSummaries": ) @classmethod - def to_summary_by_lineage(cls, *, tasks: List[Dict]) -> "TaskSummaries": + def to_summary_by_lineage( + cls, *, tasks: List[Dict], actors: List[Dict] + ) -> "TaskSummaries": """ This summarizes tasks by lineage. i.e. A task will be grouped with another task if they have the @@ -877,6 +879,8 @@ def to_summary_by_lineage(cls, *, tasks: List[Dict]) -> "TaskSummaries": if type_enum == TaskType.ACTOR_CREATION_TASK: actor_creation_task_id_for_actor_id[task["actor_id"]] = task["task_id"] + actor_dict = {actor["actor_id"]: actor for actor in actors} + def get_or_create_task_group(task_id: str) -> Optional[NestedTaskSummary]: """ Gets an already created task_group @@ -948,6 +952,7 @@ def get_or_create_actor_task_group( Returns None if there is missing data about the actor or one of its parents. """ key = f"actor:{actor_id}" + actor = actor_dict.get(actor_id) if key not in task_group_by_id: creation_task_id = actor_creation_task_id_for_actor_id.get(actor_id) creation_task = tasks_by_id.get(creation_task_id) @@ -958,8 +963,21 @@ def get_or_create_actor_task_group( # tree at that node. return None - # TODO(aguo): Get actor name from actors state-api. - [actor_name, *rest] = creation_task["func_or_class_name"].split(".") + # TODO(rickyx) + # We are using repr name for grouping actors if exists, + # else use class name. We should be using some group_name in the future. + if actor is None: + logger.debug( + f"We are missing actor info for actor {actor_id}, " + "even though creation task exists: {creation_task}" + ) + [actor_name, *rest] = creation_task["func_or_class_name"].split(".") + else: + actor_name = ( + actor["repr_name"] + if actor["repr_name"] + else actor["class_name"] + ) task_group_by_id[key] = NestedTaskSummary( name=actor_name, diff --git a/src/ray/gcs/gcs_server/gcs_actor_manager.h b/src/ray/gcs/gcs_server/gcs_actor_manager.h index 66ec7fa382ee2..dc92cf815075e 100644 --- a/src/ray/gcs/gcs_server/gcs_actor_manager.h +++ b/src/ray/gcs/gcs_server/gcs_actor_manager.h @@ -558,6 +558,7 @@ class GcsActorManager : public rpc::ActorInfoHandler { actor_delta->set_pid(actor.pid()); actor_delta->set_start_time(actor.start_time()); actor_delta->set_end_time(actor.end_time()); + actor_delta->set_repr_name(actor.repr_name()); // Acotr's namespace and name are used for removing cached name when it's dead. if (!actor.ray_namespace().empty()) { actor_delta->set_ray_namespace(actor.ray_namespace()); From 93d4ab17ba77d1a6b262dccc76a9a0e15b032fa2 Mon Sep 17 00:00:00 2001 From: rickyyx Date: Thu, 23 Mar 2023 02:31:18 +0000 Subject: [PATCH 4/6] format Signed-off-by: rickyyx --- dashboard/client/src/pages/actor/ActorDetail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard/client/src/pages/actor/ActorDetail.tsx b/dashboard/client/src/pages/actor/ActorDetail.tsx index 2e41542a3d16f..132e2322d60db 100644 --- a/dashboard/client/src/pages/actor/ActorDetail.tsx +++ b/dashboard/client/src/pages/actor/ActorDetail.tsx @@ -94,7 +94,7 @@ const ActorDetailPage = () => { }, { label: "Repr", - content: actorDetail.actorClass + content: actorDetail.reprName ? { value: actorDetail.reprName, } From 03fa4858ea51c55088b2605aa5a6fc8761f67880 Mon Sep 17 00:00:00 2001 From: rickyyx Date: Thu, 23 Mar 2023 02:34:57 +0000 Subject: [PATCH 5/6] fix Signed-off-by: rickyyx --- python/ray/experimental/state/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ray/experimental/state/common.py b/python/ray/experimental/state/common.py index a82bef0cf3e20..760301983b7ad 100644 --- a/python/ray/experimental/state/common.py +++ b/python/ray/experimental/state/common.py @@ -969,7 +969,7 @@ def get_or_create_actor_task_group( if actor is None: logger.debug( f"We are missing actor info for actor {actor_id}, " - "even though creation task exists: {creation_task}" + f"even though creation task exists: {creation_task}" ) [actor_name, *rest] = creation_task["func_or_class_name"].split(".") else: From 95287c51006570b91fa3991c5f2f0e6fed1f3669 Mon Sep 17 00:00:00 2001 From: rickyyx Date: Thu, 23 Mar 2023 06:10:31 +0000 Subject: [PATCH 6/6] fix Signed-off-by: rickyyx --- python/ray/tests/test_state_api_summary.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/ray/tests/test_state_api_summary.py b/python/ray/tests/test_state_api_summary.py index 5d209fe4e202a..c815d5f18de8e 100644 --- a/python/ray/tests/test_state_api_summary.py +++ b/python/ray/tests/test_state_api_summary.py @@ -668,7 +668,7 @@ def grab_tasks_from_task_group( random.shuffle(tasks) - summary = TaskSummaries.to_summary_by_lineage(tasks=tasks) + summary = TaskSummaries.to_summary_by_lineage(tasks=tasks, actors=[]) assert summary.total_tasks == 20 assert summary.total_actor_tasks == 110