def test_get_pipeline_input_node(): # 1) No InputNode found ray_dag = combine.bind(1, 2) with _DAGNodeNameGenerator() as node_name_generator: serve_dag = ray_dag.apply_recursive( lambda node: transform_ray_dag_to_serve_dag(node, node_name_generator) ) with pytest.raises( AssertionError, match="There should be one and only one InputNode" ): get_pipeline_input_node(serve_dag) # 2) More than one InputNode found with InputNode() as dag_input: a = combine.bind(dag_input[0], dag_input[1]) with InputNode() as dag_input_2: b = combine.bind(dag_input_2[0], dag_input_2[1]) ray_dag = combine.bind(a, b) with pytest.raises( AssertionError, match="Each DAG should only have one unique InputNode" ): with _DAGNodeNameGenerator() as node_name_generator: serve_dag = ray_dag.apply_recursive( lambda node: transform_ray_dag_to_serve_dag(node, node_name_generator) ) get_pipeline_input_node(serve_dag)
def test_wide_fanout_deployment_graph(fanout_degree, init_delay_secs=0, compute_delay_secs=0): """ Test that focuses on wide fanout of deployment graph -> Node_1 / \ INPUT --> Node_2 --> combine -> OUTPUT \ ... / -> Node_10 1) Intermediate blob size can be large / small 2) Compute time each node can be long / short 3) Init time can be long / short """ nodes = [ Node.bind(i, init_delay_secs=init_delay_secs) for i in range(0, fanout_degree) ] outputs = [] with InputNode() as user_input: for i in range(0, fanout_degree): outputs.append(nodes[i].compute.bind( user_input, compute_delay_secs=compute_delay_secs)) dag = combine.bind(outputs) serve_dag = DAGDriver.bind(dag) return serve_dag
def test_long_chain_deployment_graph( chain_length, init_delay_secs=0, compute_delay_secs=0 ): """ Test that focuses on long chain of deployment graph INPUT -> Node_1 -> Node_2 -> ... -> Node_10 -> OUTPUT 1) Intermediate blob size can be large / small 2) Compute time each node can be long / short 3) Init time can be long / short """ nodes = [Node.bind(i, init_delay_secs=init_delay_secs) for i in range(chain_length)] prev_outputs = [None for _ in range(chain_length)] with InputNode() as user_input: for i in range(chain_length): if i == 0: prev_outputs[i] = nodes[i].compute.bind( user_input, compute_delay_secs=compute_delay_secs ) else: prev_outputs[i] = nodes[i].compute.bind( prev_outputs[i - 1], compute_delay_secs=compute_delay_secs ) serve_dag = DAGDriver.bind(prev_outputs[-1]) return serve_dag
def test_dag_to_workflow_execution(workflow_start_regular_shared): """This test constructs a DAG with complex dependencies and turns it into a workflow.""" @ray.remote def begin(x, pos, a): return x * a + pos # 23.14 @ray.remote def left(x, c, a): return f"left({x}, {c}, {a})" @ray.remote def right(x, b, pos): return f"right({x}, {b}, {pos})" @ray.remote def end(lf, rt, b): return f"{lf},{rt};{b}" with pytest.raises(TypeError): workflow.create(begin.remote(1, 2, 3)) with InputNode() as dag_input: f = begin.bind(2, dag_input[1], a=dag_input.a) lf = left.bind(f, "hello", dag_input.a) rt = right.bind(f, b=dag_input.b, pos=dag_input[0]) b = end.bind(lf, rt, b=dag_input.b) wf = workflow.create(b, 2, 3.14, a=10, b="ok") assert wf.run() == "left(23.14, hello, 10),right(23.14, ok, 2);ok"
def test_single_class_with_invalid_deployment_options(serve_instance): with InputNode() as dag_input: model = Model.options(name="my_deployment").bind(2, ratio=0.3) ray_dag = model.forward.bind(dag_input) with _DAGNodeNameGenerator() as node_name_generator: serve_root_dag = ray_dag.apply_recursive( lambda node: transform_ray_dag_to_serve_dag(node, node_name_generator) ) deployments = extract_deployments_from_serve_dag(serve_root_dag) assert len(deployments) == 1 with pytest.raises( ValueError, match="Specifying 'name' in ray_actor_options is not allowed" ): deployments[0].deploy()
def test_same_object_many_dags(workflow_start_regular_shared): """Ensure that when we dedupe uploads, we upload the object once per DAG, since different DAGs shouldn't look in each others object directories. """ @ray.remote def f(a): return [a[0]] x = {0: ray.put(10)} result1 = workflow.create(f.bind(x)).run() result2 = workflow.create(f.bind(x)).run() with InputNode() as dag_input: result3 = workflow.create(f.bind(dag_input.x), x=x).run() assert ray.get(*result1) == 10 assert ray.get(*result2) == 10 assert ray.get(*result3) == 10
def test_single_class_with_valid_ray_options(serve_instance): with InputNode() as dag_input: model = Model.options(num_cpus=1, memory=1000).bind(2, ratio=0.3) ray_dag = model.forward.bind(dag_input) with _DAGNodeNameGenerator() as node_name_generator: serve_root_dag = ray_dag.apply_recursive( lambda node: transform_ray_dag_to_serve_dag(node, node_name_generator) ) deployments = extract_deployments_from_serve_dag(serve_root_dag) assert len(deployments) == 1 deployments[0].deploy() _validate_consistent_python_output( deployments[0], ray_dag, deployments[0].name, input=1, output=0.6 ) deployment = serve.get_deployment(deployments[0].name) assert deployment.ray_actor_options.get("num_cpus") == 1 assert deployment.ray_actor_options.get("memory") == 1000 assert deployment.ray_actor_options.get("runtime_env") == {}
def test_single_class_with_invalid_deployment_options(serve_instance): with pytest.raises(TypeError, match="name must be a string"): with InputNode() as dag_input: model = Model.options(name=123).bind(2, ratio=0.3) _ = model.forward.bind(dag_input)