Skip to content

Commit 33092d3

Browse files
authored
Add saga example (#17)
2 parents d9f4ad7 + 8c35f38 commit 33092d3

File tree

2 files changed

+131
-1
lines changed

2 files changed

+131
-1
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ repositories {
3333
}
3434

3535
dependencies {
36-
compile group: 'com.uber.cadence', name: 'cadence-client', version: '2.2.0'
36+
compile group: 'com.uber.cadence', name: 'cadence-client', version: '2.5.2'
3737
compile group: 'commons-configuration', name: 'commons-configuration', version: '1.9'
3838
compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
3939
testCompile group: 'junit', name: 'junit', version: '4.12'
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package com.uber.cadence.samples.hello;
2+
3+
import com.uber.cadence.activity.ActivityMethod;
4+
import com.uber.cadence.client.WorkflowClient;
5+
import com.uber.cadence.client.WorkflowOptions;
6+
import com.uber.cadence.worker.Worker;
7+
import com.uber.cadence.workflow.*;
8+
9+
import java.time.Duration;
10+
11+
import static com.uber.cadence.samples.common.SampleConstants.DOMAIN;
12+
13+
/**
14+
* Demonstrates implementing saga transaction and compensation logic using Cadence.
15+
*/
16+
public class HelloSaga {
17+
static final String TASK_LIST = "HelloSaga";
18+
19+
public interface ChildWorkflowOperation {
20+
@WorkflowMethod
21+
void execute(int amount);
22+
}
23+
24+
public static class ChildWorkflowOperationImpl implements ChildWorkflowOperation {
25+
ActivityOperation activity = Workflow.newActivityStub(ActivityOperation.class);
26+
27+
public void execute(int amount) {
28+
activity.execute(amount);
29+
}
30+
}
31+
32+
public interface ChildWorkflowCompensation {
33+
@WorkflowMethod
34+
void compensate(int amount);
35+
}
36+
37+
public static class ChildWorkflowCompensationImpl implements ChildWorkflowCompensation {
38+
ActivityOperation activity = Workflow.newActivityStub(ActivityOperation.class);
39+
40+
public void compensate(int amount) {
41+
activity.compensate(amount);
42+
}
43+
}
44+
45+
public interface ActivityOperation {
46+
@ActivityMethod(scheduleToCloseTimeoutSeconds = 2)
47+
void execute(int amount);
48+
49+
@ActivityMethod(scheduleToCloseTimeoutSeconds = 2)
50+
void compensate(int amount);
51+
}
52+
53+
public static class ActivityOperationImpl implements ActivityOperation {
54+
55+
public void execute(int amount) {
56+
System.out.println("ActivityOperationImpl.execute() is called with amount " + amount);
57+
}
58+
59+
public void compensate(int amount) {
60+
System.out.println("ActivityCompensationImpl.compensate() is called with amount " + amount);
61+
}
62+
}
63+
64+
public interface SagaWorkflow {
65+
/**
66+
* Main saga workflow.
67+
* Here we execute activity operation twice (first from a child workflow, second directly using
68+
* activity stub), add three compensation functions, and then throws some exception in workflow code.
69+
* When we catch the exception, saga.compensate will run the compensation functions according
70+
* to the policy specified in SagaOptions.
71+
*/
72+
@WorkflowMethod
73+
void execute();
74+
}
75+
76+
public static class SagaWorkflowImpl implements SagaWorkflow {
77+
ActivityOperation activity = Workflow.newActivityStub(ActivityOperation.class);
78+
79+
@Override
80+
public void execute() {
81+
Saga saga = new Saga(new Saga.Options.Builder().setParallelCompensation(false).build());
82+
try {
83+
// The following demonstrate how to compensate sync invocations.
84+
ChildWorkflowOperation op1 = Workflow.newChildWorkflowStub(ChildWorkflowOperation.class);
85+
op1.execute(10);
86+
ChildWorkflowCompensation c1 = Workflow.newChildWorkflowStub(ChildWorkflowCompensation.class);
87+
saga.addCompensation(c1::compensate, -10);
88+
89+
// The following demonstrate how to compensate async invocations.
90+
Promise<Void> result = Async.procedure(activity::execute, 20);
91+
saga.addCompensation(activity::compensate, -20);
92+
result.get();
93+
94+
// The following demonstrate the ability of supplying arbitrary lambda as a saga
95+
// compensation function. In production code please always use Workflow.getLogger
96+
// to log messages in workflow code.
97+
saga.addCompensation(() -> System.out.println("Other compensation logic in main workflow."));
98+
throw new RuntimeException("some error");
99+
100+
} catch (Exception e) {
101+
saga.compensate();
102+
}
103+
}
104+
}
105+
106+
public static void main(String[] args) {
107+
// Start a worker that hosts the workflow implementation.
108+
Worker.Factory factory = new Worker.Factory(DOMAIN);
109+
Worker worker = factory.newWorker(TASK_LIST);
110+
worker.registerWorkflowImplementationTypes(
111+
HelloSaga.SagaWorkflowImpl.class,
112+
HelloSaga.ChildWorkflowOperationImpl.class,
113+
HelloSaga.ChildWorkflowCompensationImpl.class);
114+
worker.registerActivitiesImplementations(new ActivityOperationImpl());
115+
factory.start();
116+
117+
// Start a workflow execution. Usually this is done from another program.
118+
WorkflowClient workflowClient = WorkflowClient.newInstance(DOMAIN);
119+
// Get a workflow stub using the same task list the worker uses.
120+
WorkflowOptions workflowOptions =
121+
new WorkflowOptions.Builder()
122+
.setTaskList(TASK_LIST)
123+
.setExecutionStartToCloseTimeout(Duration.ofSeconds(30))
124+
.build();
125+
HelloSaga.SagaWorkflow workflow =
126+
workflowClient.newWorkflowStub(HelloSaga.SagaWorkflow.class, workflowOptions);
127+
workflow.execute();
128+
System.exit(0);
129+
}
130+
}

0 commit comments

Comments
 (0)