Skip to content

Commit 3ebed19

Browse files
author
Tihomir Surdilovic
authored
Separated comparisons from examples and added Cadence comparison (#237)
* added comparisons folder Signed-off-by: Tihomir Surdilovic <[email protected]> * comparisons doc added Signed-off-by: Tihomir Surdilovic <[email protected]> * fix typo Signed-off-by: Tihomir Surdilovic <[email protected]>
1 parent b1c0018 commit 3ebed19

File tree

10 files changed

+3211
-2797
lines changed

10 files changed

+3211
-2797
lines changed

comparisons/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Comparisons with other workflow languages
2+
3+
Following comparison documents are available:
4+
5+
* [Argo comparison examples](comparison-argo.md)
6+
* [Brigade comparison examples](comparison-brigade.md)
7+
* [Google Cloud Workflow comparison examples](comparison-google-cloud-workflows.md)
8+
* [Temporal comparison examples](comparison-temporal.md)
9+
* [Cadence comparison examples](comparison-temporal.md)
File renamed without changes.
File renamed without changes.

comparisons/comparison-cadence.md

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
# Examples - Cadence
2+
3+
[Cadence](https://cadenceworkflow.io/) describes it solution as a fault-oblivious stateful programming model
4+
that obscures most of the complexities of building scalable distributed applications.
5+
Cadence apps are written in code, and SDKs are available for Go, Java.
6+
Its core abstraction is a fault-oblivious stateful workflow which by default encapsulates state, processing threads,
7+
durable timers and event handlers.
8+
9+
Cadence also has support for [non-programming-language workflow DSLs](https://cadenceworkflow.io/docs/use-cases/dsl/).
10+
It says that any application that interprets a workflow DSL definition can be written using the Cadence SDK, so for example
11+
BPMN, AWS Step Functions, etc. Since Cadence does not provide its own non-programming-language workflow DSL,
12+
we will only take a look at its examples written in their provided programming language support.
13+
14+
The purpose of this document is to compare and contrast the Cadence workflow code and the equivalent
15+
Serverless Workflow DSL.
16+
This can hopefully help compare and contrast the two workflow languages and give a better understanding of both.
17+
18+
Given that Cadence provides SDKs in multiple programming languages, in this document we will focus only on Cadence workflows
19+
written in Java.
20+
21+
All Cadence examples used in this document are available in their [cadence-samples](https://github.com/uber-common/cadence-samples)
22+
23+
We hope that these examples will give you a good start for comparing and contrasting the two workflow
24+
languages.
25+
26+
## Note when reading provided examples
27+
28+
Note that we do not include all the Cadence Java code from the examples, but just the main parts.
29+
Each example includes a link to the Cadence example GitHub repo where you can see all the code
30+
for the particular example.
31+
32+
## Table of Contents
33+
34+
- [Saga pattern (compensation)](#Saga-Pattern)
35+
- [File Processing](#File-Processing)
36+
- [Sub Workflow Greeting](#Sub-Workflow-Greeting)
37+
38+
### Saga Pattern
39+
40+
[Full Cadence Example](https://github.com/uber/cadence-java-samples/tree/master/src/main/java/com/uber/cadence/samples/bookingsaga)
41+
42+
<table>
43+
<tr>
44+
<th>Cadence</th>
45+
<th>Serverless Workflow</th>
46+
</tr>
47+
<tr>
48+
<td valign="top">
49+
50+
```java
51+
public class TripBookingWorkflowImpl implements TripBookingWorkflow {
52+
53+
private final ActivityOptions options =
54+
new ActivityOptions.Builder().setScheduleToCloseTimeout(Duration.ofHours(1)).build();
55+
private final TripBookingActivities activities =
56+
Workflow.newActivityStub(TripBookingActivities.class, options);
57+
58+
@Override
59+
public void bookTrip(String name) {
60+
Saga.Options sagaOptions = new Saga.Options.Builder().setParallelCompensation(true).build();
61+
Saga saga = new Saga(sagaOptions);
62+
try {
63+
String carReservationID = activities.reserveCar(name);
64+
saga.addCompensation(activities::cancelCar, carReservationID, name);
65+
66+
String hotelReservationID = activities.bookHotel(name);
67+
saga.addCompensation(activities::cancelHotel, hotelReservationID, name);
68+
69+
String flightReservationID = activities.bookFlight(name);
70+
saga.addCompensation(activities::cancelFlight, flightReservationID, name);
71+
} catch (ActivityException e) {
72+
saga.compensate();
73+
throw e;
74+
}
75+
}
76+
}
77+
```
78+
79+
</td>
80+
<td valign="top">
81+
82+
```yaml
83+
id: tripbookingwithcompensation
84+
name: Trip Booking With Compensation
85+
version: '1.0'
86+
states:
87+
- name: BookTrip
88+
type: operation
89+
start: true
90+
compensatedBy: CancelTrip
91+
actions:
92+
- functionRef: reservecarfunction
93+
- functionRef: reservehotelfunction
94+
- functionRef: reserveflightfunction
95+
onErrors:
96+
- error: Activity Error
97+
end:
98+
compensate: true
99+
end: true
100+
- name: CancelTrip
101+
type: operation
102+
usedForCompensation: true
103+
actionMode: parallel
104+
actions:
105+
- functionRef: cancelcarreservationfunction
106+
- functionRef: cancelhotelreservationfunction
107+
- functionRef: cancelflightreservationfunction
108+
functions:
109+
- name: reservecarfunction
110+
operation: myactionsapi.json#reservecar
111+
- name: reservehotelfunction
112+
operation: myactionsapi.json#reservehotel
113+
- name: reserveflightfunction
114+
operation: myactionsapi.json#reserveflight
115+
- name: cancelcarreservationfunction
116+
operation: myactionsapi.json#cancelcar
117+
- name: cancelhotelreservationfunction
118+
operation: myactionsapi.json#cancelhotel
119+
- name: cancelflightreservationfunction
120+
operation: myactionsapi.json#cancelflight
121+
```
122+
123+
</td>
124+
</tr>
125+
</table>
126+
127+
### File Processing
128+
129+
[Full Cadence Example](https://github.com/uber/cadence-java-samples/tree/master/src/main/java/com/uber/cadence/samples/fileprocessing)
130+
131+
<table>
132+
<tr>
133+
<th>Cadence</th>
134+
<th>Serverless Workflow</th>
135+
</tr>
136+
<tr>
137+
<td valign="top">
138+
139+
```java
140+
public class FileProcessingWorkflowImpl implements FileProcessingWorkflow {
141+
142+
// Uses the default task list shared by the pool of workers.
143+
private final StoreActivities defaultTaskListStore;
144+
145+
public FileProcessingWorkflowImpl() {
146+
// Create activity clients.
147+
ActivityOptions ao =
148+
new ActivityOptions.Builder()
149+
.setScheduleToCloseTimeout(Duration.ofSeconds(10))
150+
.setTaskList(FileProcessingWorker.TASK_LIST)
151+
.build();
152+
this.defaultTaskListStore = Workflow.newActivityStub(StoreActivities.class, ao);
153+
}
154+
155+
@Override
156+
public void processFile(URL source, URL destination) {
157+
RetryOptions retryOptions =
158+
new RetryOptions.Builder()
159+
.setExpiration(Duration.ofSeconds(10))
160+
.setInitialInterval(Duration.ofSeconds(1))
161+
.build();
162+
// Retries the whole sequence on any failure, potentially on a different host.
163+
Workflow.retry(retryOptions, () -> processFileImpl(source, destination));
164+
}
165+
166+
private void processFileImpl(URL source, URL destination) {
167+
StoreActivities.TaskListFileNamePair downloaded = defaultTaskListStore.download(source);
168+
169+
// Now initialize stubs that are specific to the returned task list.
170+
ActivityOptions hostActivityOptions =
171+
new ActivityOptions.Builder()
172+
.setTaskList(downloaded.getHostTaskList())
173+
.setScheduleToCloseTimeout(Duration.ofSeconds(10))
174+
.build();
175+
StoreActivities hostSpecificStore =
176+
Workflow.newActivityStub(StoreActivities.class, hostActivityOptions);
177+
178+
// Call processFile activity to zip the file.
179+
// Call the activity to process the file using worker-specific task list.
180+
String processed = hostSpecificStore.process(downloaded.getFileName());
181+
// Call upload activity to upload the zipped file.
182+
hostSpecificStore.upload(processed, destination);
183+
}
184+
}
185+
```
186+
187+
</td>
188+
<td valign="top">
189+
190+
```yaml
191+
id: fileprocessingwithretries
192+
name: File Processing Workflow With Retries
193+
version: '1.0'
194+
states:
195+
- name: ProcessAndUpload
196+
type: operation
197+
start: true
198+
actions:
199+
- functionRef:
200+
refName: processfilefunction
201+
parameters:
202+
filename: "{{ $file.name }}"
203+
actionDataFilter:
204+
dataResultsPath: "{{ $.processed }}"
205+
- functionRef:
206+
refName: uploadfunction
207+
parameters:
208+
file: "{{ $processed }}"
209+
onErrors:
210+
- error: "*"
211+
retryRef: fileprocessingretry
212+
end: true
213+
end: true
214+
functions:
215+
- name: processfilefunction
216+
operation: myactionsapi.json#process
217+
- name: uploadfilefunction
218+
operation: myactionsapi.json#upload
219+
retries:
220+
- name: fileprocessingretry
221+
maxAttempts: 10
222+
delay: PT1S
223+
```
224+
225+
</td>
226+
</tr>
227+
</table>
228+
229+
230+
### Sub Workflow Greeting
231+
232+
[Full Cadence Example](https://github.com/uber/cadence-java-samples/blob/master/src/main/java/com/uber/cadence/samples/hello/HelloChild.java)
233+
234+
<table>
235+
<tr>
236+
<th>Cadence</th>
237+
<th>Serverless Workflow</th>
238+
</tr>
239+
<tr>
240+
<td valign="top">
241+
242+
```java
243+
public static class GreetingWorkflowImpl implements GreetingWorkflow {
244+
245+
@Override
246+
public String getGreeting(String name) {
247+
// Workflows are stateful. So a new stub must be created for each new child.
248+
GreetingChild child = Workflow.newChildWorkflowStub(GreetingChild.class);
249+
250+
// This is a non blocking call that returns immediately.
251+
// Use child.composeGreeting("Hello", name) to call synchronously.
252+
Promise<String> greeting = Async.function(child::composeGreeting, "Hello", name);
253+
// Do something else here.
254+
return greeting.get(); // blocks waiting for the child to complete.
255+
}
256+
257+
// This example shows how parent workflow return right after starting a child workflow,
258+
// and let the child run itself.
259+
private String demoAsyncChildRun(String name) {
260+
GreetingChild child = Workflow.newChildWorkflowStub(GreetingChild.class);
261+
// non blocking call that initiated child workflow
262+
Async.function(child::composeGreeting, "Hello", name);
263+
// instead of using greeting.get() to block till child complete,
264+
// sometimes we just want to return parent immediately and keep child running
265+
Promise<WorkflowExecution> childPromise = Workflow.getWorkflowExecution(child);
266+
childPromise.get(); // block until child started,
267+
// otherwise child may not start because parent complete first.
268+
return "let child run, parent just return";
269+
}
270+
271+
public static void main(String[] args) {
272+
// Start a worker that hosts both parent and child workflow implementations.
273+
Worker.Factory factory = new Worker.Factory(DOMAIN);
274+
Worker worker = factory.newWorker(TASK_LIST);
275+
worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class, GreetingChildImpl.class);
276+
// Start listening to the workflow task list.
277+
factory.start();
278+
279+
// Start a workflow execution. Usually this is done from another program.
280+
WorkflowClient workflowClient = WorkflowClient.newInstance(DOMAIN);
281+
// Get a workflow stub using the same task list the worker uses.
282+
GreetingWorkflow workflow = workflowClient.newWorkflowStub(GreetingWorkflow.class);
283+
// Execute a workflow waiting for it to complete.
284+
String greeting = workflow.getGreeting("World");
285+
System.out.println(greeting);
286+
System.exit(0);
287+
}
288+
}
289+
```
290+
291+
</td>
292+
<td valign="top">
293+
294+
```yaml
295+
id: subflowgreeting
296+
name: SubFlow Greeting Workflow
297+
version: '1.0'
298+
states:
299+
- name: GreetingSubFlow
300+
type: subflow
301+
workflowId: subflowgreet
302+
start: true
303+
waitForCompletion: false
304+
end: true
305+
functions:
306+
- name: greetingfunction
307+
operation: myactionsapi.json#greet
308+
```
309+
310+
</td>
311+
</tr>
312+
</table>
313+
314+
#### Note
315+
316+
The Serverless Workflow example does not include the simple definition of the "subflowgreet", which would
317+
include just a starting operation state that executes the "greetingfunction".
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)