mirror of
https://github.com/wassname/ray.git
synced 2026-09-12 12:51:15 +08:00
Fault tolerance for actor creation (#3422)
* Add regression test * Request actor creation if no actor location found * Comments * Address comments * Increase test timeout * Trigger test
This commit is contained in:
committed by
Eric Liang
parent
fd7e494344
commit
48a5935224
@@ -5,11 +5,14 @@ from __future__ import print_function
|
||||
import os
|
||||
import json
|
||||
import signal
|
||||
import sys
|
||||
import time
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
import ray
|
||||
from ray.test.cluster_utils import Cluster
|
||||
from ray.test.test_utils import run_string_as_driver_nonblocking
|
||||
|
||||
|
||||
@@ -33,6 +36,26 @@ def shutdown_only():
|
||||
ray.shutdown()
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ray_start_cluster():
|
||||
node_args = {
|
||||
"resources": dict(CPU=8),
|
||||
"_internal_config": json.dumps({
|
||||
"initial_reconstruction_timeout_milliseconds": 1000,
|
||||
"num_heartbeats_timeout": 10
|
||||
})
|
||||
}
|
||||
# Start with 4 worker nodes and 8 cores each.
|
||||
g = Cluster(initialize_head=True, connect=True, head_node_args=node_args)
|
||||
workers = []
|
||||
for _ in range(4):
|
||||
workers.append(g.add_node(**node_args))
|
||||
g.wait_for_nodes()
|
||||
yield g
|
||||
ray.shutdown()
|
||||
g.shutdown()
|
||||
|
||||
|
||||
# This test checks that when a worker dies in the middle of a get, the plasma
|
||||
# store and raylet will not die.
|
||||
@pytest.mark.skipif(
|
||||
@@ -347,6 +370,51 @@ def test_plasma_store_failed():
|
||||
ray.shutdown()
|
||||
|
||||
|
||||
def test_actor_creation_node_failure(ray_start_cluster):
|
||||
# TODO(swang): Refactor test_raylet_failed, etc to reuse the below code.
|
||||
cluster = ray_start_cluster
|
||||
|
||||
@ray.remote
|
||||
class Child(object):
|
||||
def __init__(self, death_probability):
|
||||
self.death_probability = death_probability
|
||||
|
||||
def ping(self):
|
||||
# Exit process with some probability.
|
||||
exit_chance = np.random.rand()
|
||||
if exit_chance < self.death_probability:
|
||||
sys.exit(-1)
|
||||
|
||||
num_children = 100
|
||||
# Children actors will die about half the time.
|
||||
death_probability = 0.5
|
||||
|
||||
children = [Child.remote(death_probability) for _ in range(num_children)]
|
||||
while len(cluster.list_all_nodes()) > 1:
|
||||
for j in range(3):
|
||||
# Submit some tasks on the actors. About half of the actors will
|
||||
# fail.
|
||||
children_out = [child.ping.remote() for child in children]
|
||||
# Wait a while for all the tasks to complete. This should trigger
|
||||
# reconstruction for any actor creation tasks that were forwarded
|
||||
# to nodes that then failed.
|
||||
ready, _ = ray.wait(
|
||||
children_out,
|
||||
num_returns=len(children_out),
|
||||
timeout=5 * 60 * 1000)
|
||||
assert len(ready) == len(children_out)
|
||||
|
||||
# Replace any actors that died.
|
||||
for i, out in enumerate(children_out):
|
||||
try:
|
||||
ray.get(out)
|
||||
except ray.worker.RayGetError:
|
||||
children[i] = Child.remote(death_probability)
|
||||
# Remove a node. Any actor creation tasks that were forwarded to this
|
||||
# node must be reconstructed.
|
||||
cluster.remove_node(cluster.list_all_nodes()[-1])
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
os.environ.get("RAY_USE_NEW_GCS") == "on",
|
||||
reason="Hanging with new GCS API.")
|
||||
|
||||
Reference in New Issue
Block a user