chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:17:40 +08:00
commit f1825c8ceb
10096 changed files with 2364182 additions and 0 deletions
+440
View File
@@ -0,0 +1,440 @@
import asyncio
import json
import os
import sys
import tempfile
from collections import defaultdict
from concurrent.futures import ThreadPoolExecutor
from pathlib import Path
import pytest
import requests
import ray
from ray._common.test_utils import wait_for_condition
from ray._private.profiling import chrome_tracing_dump
from ray._private.test_utils import (
check_call_subprocess,
wait_for_aggregator_agent_if_enabled,
)
from ray.util.state import (
get_actor,
list_actors,
list_nodes,
list_tasks,
list_workers,
)
pytestmark = [
pytest.mark.parametrize(
"event_routing_config", ["default", "aggregator"], indirect=True
),
pytest.mark.usefixtures("event_routing_config"),
]
def test_timeline(shutdown_only):
ray.init(num_cpus=8)
job_id = ray.get_runtime_context().get_job_id()
TASK_SLEEP_TIME_S = 1
@ray.remote
def f():
import time
time.sleep(TASK_SLEEP_TIME_S)
@ray.remote
class Actor:
def ready(self):
pass
@ray.remote
class AsyncActor:
async def f(self):
await asyncio.sleep(5)
async def g(self):
await asyncio.sleep(5)
@ray.remote
class ThreadedActor:
def f(self):
import time
time.sleep(5)
def g(self):
import time
time.sleep(5)
[f.remote() for _ in range(4)]
a = Actor.remote()
b = AsyncActor.remote()
c = ThreadedActor.options(max_concurrency=15).remote()
[a.ready.remote() for _ in range(4)]
ray.get(b.f.remote())
[b.f.remote() for _ in range(4)]
[b.g.remote() for _ in range(4)]
[c.f.remote() for _ in range(4)]
[c.g.remote() for _ in range(4)]
result = json.loads(chrome_tracing_dump(list_tasks(detail=True)))
# ph is the type of the event
actor_to_events = defaultdict(list)
task_to_events = defaultdict(list)
index_to_workers = {}
index_to_nodes = {}
for item in result:
if item["ph"] == "M":
# metadata event
name = item["name"]
if name == "thread_name":
index_to_workers[item["tid"]] = item["args"]["name"]
elif name == "process_name":
index_to_nodes[item["pid"]] = item["args"]["name"]
else:
raise ValueError(f"Unexecpted name from metadata event {name}")
elif item["ph"] == "X":
# regular interval event
actor_id = item["args"]["actor_id"]
assert "actor_id" in item["args"]
assert "attempt_number" in item["args"]
assert "func_or_class_name" in item["args"]
assert "job_id" in item["args"]
assert "task_id" in item["args"]
if actor_id:
actor_to_events[actor_id].append(item)
else:
task_to_events[item["args"]["task_id"]].append(item)
else:
raise ValueError(f"Unexpected event type {item['ph']}")
actors = {actor["actor_id"]: actor for actor in list_actors(detail=True)}
tasks = {task["task_id"]: task for task in list_tasks(detail=True)}
workers = {worker["worker_id"]: worker for worker in list_workers(detail=True)}
nodes = {node["node_ip"]: node for node in list_nodes(detail=True)}
for actor_id, events in actor_to_events.items():
# Event type is tested from test_advanced.py::test_profiling_api
for event in events:
# Make sure actor id is correctly set.
assert event["args"]["actor_id"] == actor_id
assert event["args"]["job_id"] == job_id
task_id = event["args"]["task_id"]
assert (
event["args"]["func_or_class_name"]
== tasks[task_id]["func_or_class_name"]
) # noqa
# Make sure the worker id is correct.
# ID is recorded as [worker_type]:[worker_id]
worker_id_from_event = index_to_workers[event["tid"]].split(":")[1]
# Node is recorded as Node [ip_address]
node_id_from_event = index_to_nodes[event["pid"]].split(" ")[1]
assert actors[actor_id]["pid"] == workers[worker_id_from_event]["pid"]
assert actors[actor_id]["node_id"] == nodes[node_id_from_event]["node_id"]
for task_id, events in task_to_events.items():
for event in events:
# Make sure actor id is correctly set.
assert event["args"]["job_id"] == job_id
task_id = event["args"]["task_id"]
assert (
event["args"]["func_or_class_name"]
== tasks[task_id]["func_or_class_name"]
) # noqa
# Make sure the duration is correct.
# duration is in microseconds.
# Since the task sleeps for TASK_SLEEP_TIME_S,
# task:execute should have a similar sleep time.
if event["cat"] == "task:execute":
assert (
TASK_SLEEP_TIME_S * 1e6 * 0.9
< event["dur"]
< TASK_SLEEP_TIME_S * 1e6 * 1.1
) # noqa
# Make sure the worker id is correct.
worker_id_from_event = index_to_workers[event["tid"]].split(":")[1]
node_id_from_event = index_to_nodes[event["pid"]].split(" ")[1]
assert tasks[task_id]["worker_id"] == worker_id_from_event
assert tasks[task_id]["node_id"] == nodes[node_id_from_event]["node_id"]
# Verify the number of metadata events are correct.
metadata_events = list(filter(lambda e: e["ph"] == "M", result))
assert len(metadata_events) == len(index_to_workers) + len(index_to_nodes)
def test_timeline_request(shutdown_only):
context = ray.init()
dashboard_url = f"http://{context['webui_url']}"
@ray.remote
def f():
pass
ray.get([f.remote() for _ in range(5)])
# Make sure the API works.
def verify():
resp = requests.get(f"{dashboard_url}/api/v0/tasks/timeline")
resp.raise_for_status()
assert resp.json(), "No result has returned"
return True
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")
def test_experimental_import_deprecation():
for name in list(sys.modules):
if name.startswith("ray.experimental.state"):
sys.modules.pop(name, None)
with pytest.warns(DeprecationWarning):
from ray.experimental.state.api import list_tasks # noqa: F401
with pytest.warns(DeprecationWarning):
from ray.experimental.state.common import DEFAULT_RPC_TIMEOUT # noqa: F401
with pytest.warns(DeprecationWarning):
from ray.experimental.state.custom_types import ACTOR_STATUS # noqa: F401
with pytest.warns(DeprecationWarning):
from ray.experimental.state.exception import RayStateApiException # noqa: F401
with pytest.warns(DeprecationWarning):
from ray.experimental.state.state_cli import ray_get # noqa: F401
with pytest.warns(DeprecationWarning):
from ray.experimental.state.state_manager import ( # noqa: F401
StateDataSourceClient,
)
with pytest.warns(DeprecationWarning):
from ray.experimental.state.util import convert_string_to_type # noqa: F401
def test_actor_task_with_repr_name(ray_start_with_dashboard):
wait_for_aggregator_agent_if_enabled(
ray_start_with_dashboard["gcs_address"], ray_start_with_dashboard["node_id"]
)
@ray.remote
class ReprActor:
def __init__(self, x) -> None:
self.x = x
def __repr__(self) -> str:
return self.x
def f(self):
pass
a = ReprActor.remote(x="repr-name-a")
ray.get(a.f.remote())
def verify():
tasks = list_tasks(detail=True, filters=[("type", "=", "ACTOR_TASK")])
assert len(tasks) == 1, tasks
assert tasks[0].name == "repr-name-a.f"
assert tasks[0].func_or_class_name == "ReprActor.f"
return True
wait_for_condition(verify)
b = ReprActor.remote(x="repr-name-b")
ray.get(b.f.options(name="custom-name").remote())
def verify():
tasks = list_tasks(
detail=True,
filters=[("actor_id", "=", b._actor_id.hex()), ("type", "=", "ACTOR_TASK")],
)
assert len(tasks) == 1, tasks
assert tasks[0].name == "custom-name"
assert tasks[0].func_or_class_name == "ReprActor.f"
return True
wait_for_condition(verify)
@ray.remote
class Actor:
def f(self):
pass
c = Actor.remote()
ray.get(c.f.remote())
def verify():
tasks = list_tasks(
detail=True,
filters=[("actor_id", "=", c._actor_id.hex()), ("type", "=", "ACTOR_TASK")],
)
assert len(tasks) == 1, tasks
assert tasks[0].name == "Actor.f"
assert tasks[0].func_or_class_name == "Actor.f"
return True
wait_for_condition(verify)
@pytest.mark.skipif(
sys.platform == "win32", reason="Release test not expected to work on non-linux."
)
def test_state_api_scale_smoke(shutdown_only, monkeypatch):
address_info = ray.init()
wait_for_aggregator_agent_if_enabled(
address_info["gcs_address"], address_info["node_id"]
)
monkeypatch.setenv("RAY_ADDRESS", address_info["gcs_address"])
release_test_file_path = (
"../../release/nightly_tests/stress_tests/test_state_api_scale.py"
)
full_path = Path(ray.__file__).parents[0] / release_test_file_path
assert full_path.exists()
check_call_subprocess(["python", str(full_path), "--smoke-test"])
def test_ray_timeline(shutdown_only):
context = ray.init(num_cpus=8)
wait_for_aggregator_agent_if_enabled(context["gcs_address"], context["node_id"])
@ray.remote
def f():
import time
time.sleep(0.1)
ray.get(f.remote())
with tempfile.TemporaryDirectory() as tmpdirname:
filename = os.path.join(tmpdirname, "timeline.json")
def verify():
ray.timeline(filename)
with open(filename, "r") as timeline_file:
dumped = json.load(timeline_file)
# TODO(swang): Check actual content. It doesn't seem to match the
# return value of chrome_tracing_dump in above tests?
assert len(dumped) > 0
return True
wait_for_condition(verify, timeout=20, retry_interval_ms=1000)
def test_state_init_multiple_threads(shutdown_only):
ray.init()
global_state = ray._private.state.state
global_state._connect_and_get_accessor()
gcs_options = global_state.gcs_options
def disconnect():
global_state.disconnect()
global_state._initialize_global_state(gcs_options)
return True
def get_nodes_from_state_api():
try:
return len(global_state.node_table()) == 1
except ray.exceptions.RaySystemError:
# There's a gap between disconnect and _initialize_global_state
# and this will be raised if we try to connect during that gap
return True
disconnect()
with ThreadPoolExecutor(max_workers=50) as executor:
futures = [executor.submit(get_nodes_from_state_api) for _ in range(50)]
futures.extend([executor.submit(disconnect) for _ in range(50)])
futures.extend([executor.submit(get_nodes_from_state_api) for _ in range(50)])
results = [future.result() for future in futures]
# Assert that all calls returned True
assert all(results)
assert len(results) == 150
if __name__ == "__main__":
sys.exit(pytest.main(["-sv", __file__]))