270 lines
8.3 KiB
Python
270 lines
8.3 KiB
Python
import sys
|
|
import threading
|
|
|
|
import pytest
|
|
|
|
import ray
|
|
from ray.tests.test_memory_pressure import (
|
|
Leaker,
|
|
allocate_memory,
|
|
get_additional_bytes_to_reach_memory_usage_pct,
|
|
memory_monitor_refresh_ms,
|
|
memory_usage_threshold,
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def ray_with_memory_monitor(shutdown_only):
|
|
with ray.init(
|
|
num_cpus=1,
|
|
object_store_memory=100 * 1024 * 1024,
|
|
_system_config={
|
|
"memory_usage_threshold": memory_usage_threshold,
|
|
"memory_monitor_refresh_ms": memory_monitor_refresh_ms,
|
|
"metrics_report_interval_ms": 100,
|
|
"task_failure_entry_ttl_ms": 2 * 60 * 1000,
|
|
"task_oom_retries": 0,
|
|
"min_memory_free_bytes": -1,
|
|
},
|
|
) as addr:
|
|
yield addr
|
|
|
|
|
|
# Utility for allocating locally (ray not involved)
|
|
def alloc_mem(bytes):
|
|
chunks = 10
|
|
mem = []
|
|
bytes_per_chunk = bytes // 8 // chunks
|
|
for _ in range(chunks):
|
|
mem.append([0] * bytes_per_chunk)
|
|
return mem
|
|
|
|
|
|
@ray.remote
|
|
def task_with_nested_actor(
|
|
first_fraction, second_fraction, leaker, actor_allocation_first=True
|
|
):
|
|
first_bytes = get_additional_bytes_to_reach_memory_usage_pct(first_fraction)
|
|
second_bytes = (
|
|
get_additional_bytes_to_reach_memory_usage_pct(second_fraction) - first_bytes
|
|
)
|
|
if actor_allocation_first:
|
|
ray.get(leaker.allocate.remote(first_bytes))
|
|
dummy = alloc_mem(second_bytes)
|
|
else:
|
|
dummy = alloc_mem(first_bytes)
|
|
ray.get(leaker.allocate.remote(second_bytes))
|
|
return dummy[0]
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform != "linux" and sys.platform != "linux2",
|
|
reason="memory monitor only on linux currently",
|
|
)
|
|
def test_churn_long_running(
|
|
ray_with_memory_monitor,
|
|
):
|
|
long_running_bytes = get_additional_bytes_to_reach_memory_usage_pct(
|
|
memory_usage_threshold - 0.1
|
|
)
|
|
ray.get(
|
|
allocate_memory.options(max_retries=0).remote(
|
|
long_running_bytes, post_allocate_sleep_s=30
|
|
)
|
|
)
|
|
small_bytes = get_additional_bytes_to_reach_memory_usage_pct(
|
|
memory_usage_threshold + 0.2
|
|
)
|
|
with pytest.raises(ray.exceptions.OutOfMemoryError) as _:
|
|
ray.get(allocate_memory.options(max_retries=0).remote(small_bytes))
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform != "linux" and sys.platform != "linux2",
|
|
reason="memory monitor only on linux currently",
|
|
)
|
|
def test_deadlock_task_with_nested_task(
|
|
ray_with_memory_monitor,
|
|
):
|
|
task_bytes = get_additional_bytes_to_reach_memory_usage_pct(
|
|
memory_usage_threshold - 0.1
|
|
)
|
|
nested_task_bytes = (
|
|
get_additional_bytes_to_reach_memory_usage_pct(memory_usage_threshold + 0.2)
|
|
- task_bytes
|
|
)
|
|
with pytest.raises(ray.exceptions.RayTaskError) as _:
|
|
ray.get(
|
|
task_with_nested_task.options(max_retries=0).remote(
|
|
task_bytes=task_bytes, nested_task_bytes=nested_task_bytes, barrier=None
|
|
)
|
|
)
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform != "linux" and sys.platform != "linux2",
|
|
reason="memory monitor only on linux currently",
|
|
)
|
|
def test_deadlock_task_with_nested_actor_with_actor_first(
|
|
ray_with_memory_monitor,
|
|
):
|
|
leaker = Leaker.options(max_restarts=0, max_task_retries=0).remote()
|
|
with pytest.raises(ray.exceptions.OutOfMemoryError) as _:
|
|
ray.get(
|
|
task_with_nested_actor.remote(
|
|
first_fraction=memory_usage_threshold - 0.1,
|
|
second_fraction=memory_usage_threshold + 0.25,
|
|
leaker=leaker,
|
|
actor_allocation_first=True,
|
|
)
|
|
)
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform != "linux" and sys.platform != "linux2",
|
|
reason="memory monitor only on linux currently",
|
|
)
|
|
def test_deadlock_task_with_nested_actor_with_actor_last(
|
|
ray_with_memory_monitor,
|
|
):
|
|
leaker = Leaker.options(max_restarts=0, max_task_retries=0).remote()
|
|
with pytest.raises(ray.exceptions.OutOfMemoryError) as _:
|
|
ray.get(
|
|
task_with_nested_actor.remote(
|
|
first_fraction=memory_usage_threshold - 0.1,
|
|
second_fraction=memory_usage_threshold + 0.25,
|
|
leaker=leaker,
|
|
actor_allocation_first=False,
|
|
)
|
|
)
|
|
|
|
|
|
# Used for syncing allocations
|
|
@ray.remote
|
|
class BarrierActor:
|
|
def __init__(self, num_objects):
|
|
self.barrier = threading.Barrier(num_objects, timeout=30)
|
|
|
|
def wait_all_done(self):
|
|
self.barrier.wait()
|
|
|
|
|
|
@ray.remote
|
|
class ActorWithNestedTask:
|
|
def __init__(self, barrier=None):
|
|
self.mem = []
|
|
self.barrier = barrier
|
|
|
|
def perform_allocations(self, actor_bytes, nested_task_bytes):
|
|
self.mem = alloc_mem(actor_bytes)
|
|
if self.barrier:
|
|
ray.get(self.barrier.wait_all_done.remote())
|
|
ray.get(allocate_memory.options(max_retries=0).remote(nested_task_bytes))
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform != "linux" and sys.platform != "linux2",
|
|
reason="memory monitor only on linux currently",
|
|
)
|
|
def test_deadlock_actor_with_nested_task(
|
|
ray_with_memory_monitor,
|
|
):
|
|
leaker = ActorWithNestedTask.options(max_restarts=0, max_task_retries=0).remote()
|
|
actor_bytes = get_additional_bytes_to_reach_memory_usage_pct(
|
|
memory_usage_threshold - 0.1
|
|
)
|
|
nested_task_bytes = (
|
|
get_additional_bytes_to_reach_memory_usage_pct(memory_usage_threshold + 0.1)
|
|
- actor_bytes
|
|
)
|
|
with pytest.raises(ray.exceptions.RayTaskError) as _:
|
|
ray.get(leaker.perform_allocations.remote(actor_bytes, nested_task_bytes))
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform != "linux" and sys.platform != "linux2",
|
|
reason="memory monitor only on linux currently",
|
|
)
|
|
def test_deadlock_two_sets_of_actor_with_nested_task(
|
|
ray_with_memory_monitor,
|
|
):
|
|
barrier = BarrierActor.options(
|
|
max_restarts=0, max_task_retries=0, max_concurrency=2
|
|
).remote(2)
|
|
leaker1 = ActorWithNestedTask.options(max_restarts=0, max_task_retries=0).remote(
|
|
barrier
|
|
)
|
|
leaker2 = ActorWithNestedTask.options(max_restarts=0, max_task_retries=0).remote(
|
|
barrier
|
|
)
|
|
parent_bytes = get_additional_bytes_to_reach_memory_usage_pct(
|
|
(memory_usage_threshold - 0.05) / 2
|
|
)
|
|
nested_bytes = (
|
|
get_additional_bytes_to_reach_memory_usage_pct(memory_usage_threshold + 0.1)
|
|
- 2 * parent_bytes
|
|
)
|
|
ref1 = leaker1.perform_allocations.remote(parent_bytes, nested_bytes)
|
|
ref2 = leaker2.perform_allocations.remote(parent_bytes, nested_bytes)
|
|
|
|
with pytest.raises(ray.exceptions.RayTaskError) as _:
|
|
ray.get(ref1)
|
|
with pytest.raises(ray.exceptions.RayTaskError) as _:
|
|
ray.get(ref2)
|
|
|
|
|
|
@ray.remote
|
|
def task_with_nested_task(task_bytes, nested_task_bytes, barrier=None):
|
|
dummy = alloc_mem(task_bytes)
|
|
if barrier:
|
|
ray.get(barrier.wait_all_done.remote())
|
|
ray.get(
|
|
allocate_memory.options(max_retries=0).remote(
|
|
nested_task_bytes, post_allocate_sleep_s=0.1
|
|
)
|
|
)
|
|
return dummy[0]
|
|
|
|
|
|
@pytest.mark.skipif(
|
|
sys.platform != "linux" and sys.platform != "linux2",
|
|
reason="memory monitor only on linux currently",
|
|
)
|
|
def test_deadlock_two_sets_of_task_with_nested_task(
|
|
ray_with_memory_monitor,
|
|
):
|
|
"""task_with_nested_task allocates a block of memory, then runs
|
|
a nested task which also allocates a block memory.
|
|
This test runs two instances of task_with_nested_task.
|
|
We expect the second one to fail."""
|
|
parent_bytes = get_additional_bytes_to_reach_memory_usage_pct(
|
|
(memory_usage_threshold - 0.05) / 2
|
|
)
|
|
nested_bytes = (
|
|
get_additional_bytes_to_reach_memory_usage_pct(memory_usage_threshold + 0.1)
|
|
- 2 * parent_bytes
|
|
)
|
|
|
|
barrier = BarrierActor.options(
|
|
max_restarts=0, max_task_retries=0, max_concurrency=2
|
|
).remote(2)
|
|
|
|
ref1 = task_with_nested_task.options(max_retries=0).remote(
|
|
parent_bytes, nested_bytes, barrier
|
|
)
|
|
ref2 = task_with_nested_task.options(max_retries=0).remote(
|
|
parent_bytes, nested_bytes, barrier
|
|
)
|
|
|
|
with pytest.raises(ray.exceptions.RayTaskError) as _:
|
|
ray.get(ref1)
|
|
with pytest.raises(ray.exceptions.RayTaskError) as _:
|
|
ray.get(ref2)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
pass
|
|
# Issue #33491, timing out right now.
|
|
# See python/ray/tests/BUILD for more details (ctrl+f test_memory_deadlock)
|
|
# sys.exit(pytest.main(["-sv", __file__]))
|