import sys import numpy as np import pytest import ray from ray._private.test_utils import get_other_nodes @pytest.mark.parametrize( "ray_start_cluster", [{"num_cpus": 4, "num_nodes": 3, "do_init": True}], indirect=True, ) 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: 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 = 25 # 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(2): # 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.0 ) assert len(ready) == len(children_out) # Replace any actors that died. for i, out in enumerate(children_out): try: ray.get(out) except ray.exceptions.RayActorError: children[i] = Child.remote(death_probability) # Remove a node. Any actor creation tasks that were forwarded to this # node must be resubmitted. cluster.remove_node(get_other_nodes(cluster, True)[-1]) if __name__ == "__main__": sys.exit(pytest.main(["-sv", __file__]))