Skip to content

Commit 902b996

Browse files
author
chuhan.ly
committed
1. Delete DAGDriver and InputNode. 2. Add DeploymentHandle and DeploymentResponse demo. 3. Adjusting the way Python deploys deployment for other languages.
Signed-off-by: chuhan.ly <[email protected]>
1 parent ad08be4 commit 902b996

File tree

1 file changed

+29
-133
lines changed

1 file changed

+29
-133
lines changed

reps/2023-08-18-serve-java-dag-api.md

Lines changed: 29 additions & 133 deletions
Original file line numberDiff line numberDiff line change
@@ -15,19 +15,16 @@ def preprocess(inp: int) -> int:
1515

1616
@serve.deployment
1717
class Model:
18-
def __init__(self, increment: int):
18+
def __init__(self, preprocess_handle: RayServeHandle, increment: int):
19+
self.preprocess_handle: DeploymentHandle = preprocess_handle.options(use_new_handle_api=True)
1920
self.increment = increment
2021

21-
def predict(self, inp: int) -> int:
22-
return inp + self.increment
22+
async def predict(self, inp: int) -> int:
23+
preprocessed = await self.preprocess_handle.remote(inp)
24+
return preprocessed + self.increment
2325

24-
25-
with InputNode() as inp:
26-
model = Model.bind(increment=2)
27-
output = model.predict.bind(preprocess.bind(inp))
28-
serve_dag = DAGDriver.bind(output)
29-
30-
handle = serve.run(serve_dag)
26+
app = Model.bind(preprocess.bind(), increment=2)
27+
handle = serve.run(app)
3128
assert ray.get(handle.predict.remote(1)) == 4
3229

3330
```
@@ -37,14 +34,14 @@ Main `ray` project. A part of java/serve.
3734
## Stewardship
3835

3936
### Required Reviewers
40-
@sihanwang41
37+
@sihanwang41 @edoakes
4138

4239
### Shepherd of the Proposal (should be a senior committer)
43-
@sihanwang41
40+
@sihanwang41 @edoakes
4441

4542
## Design and Architecture
4643

47-
### Update Java user API to be consistent with Python
44+
### Update Java User API to be Consistent with Python
4845
A standard Java deployment demo is shown below:
4946
```java
5047
// Demo 1
@@ -73,27 +70,27 @@ public class DeploymentDemo {
7370

7471
```
7572
In this demo, a DAG node is defined through the `bind` method of the Deployment, and it is deployed using the `Serve.run` API.
76-
Furthermore, a Deployment can bind other Deployments, and users can use the Deployment input parameters in a similar way to `RayServeHandle`. For example:
73+
Furthermore, a Deployment can bind other Deployments, and users can use the Deployment input parameters in a similar way to `DeploymentHandle`. For example:
7774
```java
7875
// Demo 2
79-
import io.ray.api.ObjectRef;
8076
import io.ray.serve.api.Serve;
8177
import io.ray.serve.deployment.Application;
82-
import io.ray.serve.handle.RayServeHandle;
78+
import io.ray.serve.handle.DeploymentHandle;
79+
import io.ray.serve.handle.DeploymentResponse;
8380

8481
public class Driver {
85-
private RayServeHandle modelAHandle;
86-
private RayServeHandle modelBHandle;
82+
private DeploymentHandle modelAHandle;
83+
private DeploymentHandle modelBHandle;
8784

88-
public Driver(RayServeHandle modelAHandle, RayServeHandle modelBHandle) {
85+
public Driver(DeploymentHandle modelAHandle, DeploymentHandle modelBHandle) {
8986
this.modelAHandle = modelAHandle;
9087
this.modelBHandle = modelBHandle;
9188
}
9289

9390
public String call(String request) {
94-
ObjectRef<Object> refA = modelAHandle.remote(request);
95-
ObjectRef<Object> refB = modelBHandle.remote(request);
96-
return (String) refA.get() + refB.get();
91+
DeploymentResponse responseA = modelAHandle.remote(request);
92+
DeploymentResponse responseB = modelBHandle.remote(request);
93+
return (String) responseA.result() + responseB.result();
9794
}
9895

9996
public static class ModelA {
@@ -119,123 +116,22 @@ public class Driver {
119116
}
120117

121118
```
122-
In this example, modelA and modelB are defined as two Deployments, and driver is instantiated with the corresponding `RayServeHandle` of these two Deployments. When `call` is executed, both models are invoked. Additionally, more complex graphs can be composed, for example:
123-
```python
124-
def preprocess(inp: int) -> int:
125-
return inp + 1
126-
127-
```
128-
129-
```java
130-
// Demo 3
131-
import io.ray.serve.api.Serve;
132-
import io.ray.serve.deployment.Application;
133-
import io.ray.serve.deployment.DAGDriver;
134-
import io.ray.serve.deployment.InputNode;
135-
import io.ray.serve.generated.DeploymentLanguage;
136-
import io.ray.serve.handle.RayServeHandle;
137-
import java.util.concurrent.atomic.AtomicInteger;
138-
import org.testng.Assert;
139-
140-
public class Model {
141-
private AtomicInteger increment;
142-
143-
public Model(int increment) {
144-
this.increment = new AtomicInteger(increment);
145-
}
146-
147-
public int predict(int inp) {
148-
return inp + increment.get();
149-
}
150-
151-
public static void main(String[] args) throws Exception {
152-
try (InputNode inp = new InputNode()) {
153-
Application model =
154-
Serve.deployment()
155-
.setDeploymentDef(Model.class.getName())
156-
.bind(2);
157-
Application pyPreprocess =
158-
Serve.deployment()
159-
.setDeploymentDef("deployment_graph.preprocess")
160-
.setLanguage(DeploymentLanguage.PYTHON)
161-
.bind(inp);
162-
Application output = model.method("predict").bind(pyPreprocess);
163-
Application serveDag = DAGDriver.bind(output);
164-
165-
RayServeHandle handle = Serve.run(serveDag);
166-
Assert.assertEquals(handle.method("predict").remote(1).get(), 4);
167-
}
168-
}
169-
}
170-
171-
```
172-
In this case, two deployments are defined:
173-
* model: a Java Deployment where the `predict` method takes an integer input and performs addition with the initialized value.
174-
* pyPreprocess: a Python deployment that adds one to the input parameter.
175-
176-
During the graph composition, the output of pyPreprocess will be used as input to the `model.predict` method. `DAGDriver` acts as the Ingress Deployment and orchestrates the entire graph. Finally, the graph is deployed using `Serve.run`.
177-
178-
One more thing to note is the usage of `InputNode`. In Python, `InputNode` is very flexible and can represent a list, a dict, or structured object. However, in Java, it is difficult to simulate the invocation of arbitrary objects using `InputNode`, so we have made some compromises. We can simulate the invocation of a List or a Map using the `InputNode.get` method. As for structured objects, the only option is to pass the entire `InputNode` as a parameter. Here's an example:
179-
```java
180-
// Demo 4
181-
import io.ray.serve.api.Serve;
182-
import io.ray.serve.deployment.Application;
183-
import io.ray.serve.deployment.DAGDriver;
184-
import io.ray.serve.deployment.InputNode;
185-
import io.ray.serve.generated.DeploymentLanguage;
186-
import io.ray.serve.handle.RayServeHandle;
187-
188-
public class Model {
189-
190-
private int weight;
191-
192-
public Model() {}
193-
194-
public Model(int weight) {
195-
this.weight = weight;
196-
}
119+
In this example, the modelA and modelB are defined as two Deployments, and the driver is instantiated with the corresponding `DeploymentHandle` of these two Deployments. When `call` is executed, both models are invoked. Additionally, it is evident that `DeploymentHandle.remote` returns `DeploymentResponse` instead of `ObjectRef`. The result can be accessed through `DeploymentResponse.result`.
197120

198-
public int forward(int input) {
199-
return weight + input;
200-
}
201-
202-
public static void main(String[] args) throws Exception {
203-
Application m1 = Serve.deployment().setDeploymentDef(Model.class.getName()).bind(1);
204-
Application m2 = Serve.deployment().setDeploymentDef(Model.class.getName()).bind(2);
205-
206-
try (InputNode userInput = InputNode.create()) {
207-
Application m1Output = m1.method("forward").bind(userInput.get(0));
208-
Application m2Output = m2.method("forward").bind(userInput.get(1));
209-
Application combineOutput =
210-
Serve.deployment()
211-
.setDeploymentDef("deployment_graph.combine")
212-
.setLanguage(DeploymentLanguage.PYTHON)
213-
.bind(m1Output, m2Output, userInput.get(2));
121+
### Deploying Deployments of the Other Languages through Python API
122+
In another REP ([Add Cpp Deployment in Ray Serve](https://github.com/ray-project/enhancements/pull/34)), it is mentioned how to deploy C++ deployments through Python. Deploying Java deployments through Python is similar. Since Java and C++ do not have the decorator mechanism like Python, a straightforward way is to directly use the `serve.deployment` API (with the addition of a new `language` parameter):
214123

215-
Application graph = DAGDriver.bind(combineOutput);
216-
RayServeHandle handle = Serve.run(graph);
217-
}
218-
}
219-
}
124+
```python
125+
deployment = serve.deployment('io.ray.serve.ExampleDeployment', name='my_deployment', language=JAVA)
220126

221127
```
222-
### Cross-language transmission of DAGDriver
223-
In the above examples, both Demo 1 and Demo 2 are deployments of regular Java applications, which are relatively easy to implement. However, for Demo 3 and Demo 4, they involve Python `DAGDriver`, where the input of `DAGDriver` may contain `RayServeDAGHandle` that carries information for graph execution. In order to fully support graph execution orchestration through Python `DAGDriver`, it would require support for cross-language transmission of several internal core types, such as `RayServeDAGHandle`, `DeploymentMethodExecutorNode`, `DeploymentFunctionExecutorNode`, `InputAttributeNode`, and `InputNode`. This could be a significant change and needs further evaluation.
224-
225-
### Deploying deployments of other languages through Python API
226-
In another REP ([Add Cpp Deployment in Ray Serve](https://github.com/ray-project/enhancements/pull/34)), it is mentioned how to deploy C++ deployments through Python. Deploying Java deployments through Python is similar. Since Java and C++ do not have the decorator mechanism like Python, a straightforward way is to directly instantiate the corresponding Deployment object:
128+
### Deploying through the Config File
129+
// TODO
227130

228-
```python
229-
deployment_config = DeploymentConfig()
230-
deployment_config.deployment_language = JAVA # or CPP
131+
### Serve Handle C++ Core
231132

232-
deployment = Deployment(_func_or_class='io.ray.serve.ExampleDeployment', name='my_deployment', config=config)
233-
```
234-
Alternatively, you can directly use the deployment API (with the addition of a new `language` parameter):
235-
```python
236-
deployment = serve.deployment(_func_or_class='io.ray.serve.ExampleDeployment', name='my_deployment', language=JAVA)
133+
In the design of C++ Deployment, it also includes the C++ implementation of Serve Handle. After the implementation, it can be reused as the core of Serve Handle by other languages (Python and Java) to avoid maintaining duplicate logic in the three languages. For the complete design, we will continue to supplement it in the "[Cpp Deployment Design](https://github.com/ray-project/enhancements/pull/34)" or another new document.
237134

238-
```
239135
## Compatibility, Deprecation, and Migration Plan
240136
In Java, the old API will be marked with the @Deprecated annotation, for example:
241137
```java
@@ -263,4 +159,4 @@ Related test cases will be provided under ray/java/serve, and they will cover th
263159
## (Optional) Follow-on Work
264160
- Modify the Ray Serve Java API to support the usage of DAGs.
265161
- Optimize the code by removing unused components and improving cross-language parameter handling.
266-
- Support the usage of streaming.
162+
- Support the usage of streaming.

0 commit comments

Comments
 (0)