Skip to content

Commit 88c8121

Browse files
committed
Add shadowing samples
1 parent 7589265 commit 88c8121

File tree

5 files changed

+150
-1
lines changed

5 files changed

+150
-1
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ repositories {
3535
dependencies {
3636
errorproneJavac("com.google.errorprone:javac:9+181-r4173-1")
3737
errorprone("com.google.errorprone:error_prone_core:2.3.1")
38-
compile group: 'com.uber.cadence', name: 'cadence-client', version: '3.0.0'
38+
compile group: 'com.uber.cadence', name: 'cadence-client', version: '3.1.0'
3939
compile group: 'commons-configuration', name: 'commons-configuration', version: '1.9'
4040
compile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
4141
compile group: 'com.uber.m3', name: 'tally-core', version: '0.10.0'

src/main/java/com/uber/cadence/samples/hello/HelloAsync.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ public static class GreetingWorkflowImpl implements GreetingWorkflow {
6666

6767
@Override
6868
public String getGreeting(String name) {
69+
6970
// Async.invoke takes method reference and activity parameters and returns Promise.
7071
Promise<String> hello = Async.function(activities::composeGreeting, "Hello", name);
7172
Promise<String> bye = Async.function(activities::composeGreeting, "Bye", name);
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
##Shadowing example:
2+
3+
Shadowing worker uses for detecting workflow non-deterministic error
4+
prior to the workflow code deployment to prod.
5+
6+
More detail can be found: [design doc](https://github.com/uber/cadence/blob/master/docs/design/workflow-shadowing/2547-workflow-shadowing.md)
7+
8+
1. To run this example, start a 0.21+ cadence server.
9+
10+
2. Run a few HelloActivity workflow to generate workflow records.
11+
```
12+
./gradlew -q execute -PmainClass=com.uber.cadence.samples.hello.HelloActivity
13+
```
14+
15+
3. Run the traffic shadowing
16+
```
17+
./gradlew -q execute -PmainClass=com.uber.cadence.samples.shadowing.ShadowTraffic
18+
```
19+
20+
4. No non-deterministic error is expected in the stdout.
21+
22+
5. Add a non backward compatible change to HelloActivity.
23+
```
24+
for example: add a timer between workflow start and activity schedule
25+
26+
Workflow.sleep(1000);
27+
28+
```
29+
30+
6. Run the traffic shadowing
31+
```
32+
./gradlew -q execute -PmainClass=com.uber.cadence.samples.shadowing.ShadowTraffic
33+
```
34+
35+
7. Non-deterministic error is expected in the stdout.
36+
37+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.uber.cadence.samples.shadowing;
2+
3+
import com.google.common.collect.Lists;
4+
import com.uber.cadence.client.WorkflowClient;
5+
import com.uber.cadence.client.WorkflowClientOptions;
6+
import com.uber.cadence.samples.hello.HelloActivity;
7+
import com.uber.cadence.serviceclient.ClientOptions;
8+
import com.uber.cadence.serviceclient.WorkflowServiceTChannel;
9+
import com.uber.cadence.shadower.ExitCondition;
10+
import com.uber.cadence.shadower.Mode;
11+
import com.uber.cadence.worker.ShadowingOptions;
12+
import com.uber.cadence.worker.ShadowingWorker;
13+
import com.uber.cadence.worker.WorkerOptions;
14+
import com.uber.cadence.worker.WorkflowStatus;
15+
16+
import java.util.concurrent.CountDownLatch;
17+
18+
import static com.uber.cadence.samples.common.SampleConstants.DOMAIN;
19+
20+
public class ShadowTraffic {
21+
public static void main(String[] args) throws InterruptedException {
22+
// Get a new client
23+
// NOTE: to set a different options, you can do like this:
24+
// ClientOptions.newBuilder().setRpcTimeout(5 * 1000).build();
25+
WorkflowClient workflowClient =
26+
WorkflowClient.newInstance(
27+
new WorkflowServiceTChannel(ClientOptions.defaultInstance()),
28+
WorkflowClientOptions.newBuilder().setDomain(DOMAIN).build());
29+
ShadowingOptions options = ShadowingOptions
30+
.newBuilder()
31+
.setDomain(DOMAIN)
32+
.setShadowMode(Mode.Normal)
33+
.setWorkflowTypes(Lists.newArrayList("GreetingWorkflow::getGreeting"))
34+
.setWorkflowStatuses(Lists.newArrayList(WorkflowStatus.OPEN, WorkflowStatus.CLOSED))
35+
.setExitCondition(new ExitCondition().setExpirationIntervalInSeconds(60))
36+
.build();
37+
38+
ShadowingWorker shadowingWorker = new ShadowingWorker(
39+
workflowClient,
40+
"HelloActivity",
41+
WorkerOptions.defaultInstance(),
42+
options);
43+
shadowingWorker.registerWorkflowImplementationTypes(HelloActivity.GreetingWorkflowImpl.class);
44+
45+
CountDownLatch latch = new CountDownLatch(1);
46+
// Execute a workflow waiting for it to complete.
47+
Runnable runnable = () -> {
48+
try {
49+
shadowingWorker.start();
50+
} catch (Exception e) {
51+
System.out.println("Failed to start shadowing workflow");
52+
System.out.println(e);
53+
latch.countDown();
54+
}
55+
};
56+
runnable.run();
57+
latch.await();
58+
}
59+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Modifications copyright (C) 2017-2021 Uber Technologies, Inc.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
7+
* use this file except in compliance with the License. A copy of the License is
8+
* located at
9+
*
10+
* http://aws.amazon.com/apache2.0
11+
*
12+
* or in the "license" file accompanying this file. This file is distributed on
13+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
14+
* express or implied. See the License for the specific language governing
15+
* permissions and limitations under the License.
16+
*/
17+
18+
package com.uber.cadence.samples.hello;
19+
20+
import com.google.common.collect.Lists;
21+
import com.uber.cadence.serviceclient.ClientOptions;
22+
import com.uber.cadence.serviceclient.IWorkflowService;
23+
import com.uber.cadence.serviceclient.WorkflowServiceTChannel;
24+
import com.uber.cadence.shadower.ExitCondition;
25+
import com.uber.cadence.shadower.Mode;
26+
import com.uber.cadence.testing.WorkflowShadower;
27+
import com.uber.cadence.worker.ShadowingOptions;
28+
import com.uber.cadence.worker.WorkflowStatus;
29+
import org.junit.Test;
30+
31+
import static com.uber.cadence.samples.common.SampleConstants.DOMAIN;
32+
import static com.uber.cadence.samples.hello.HelloActivity.TASK_LIST;
33+
34+
public class HelloWorkflowShadowingTest {
35+
@Test
36+
public void testShadowing() throws Throwable {
37+
IWorkflowService service = new WorkflowServiceTChannel(ClientOptions.defaultInstance());
38+
39+
ShadowingOptions options = ShadowingOptions
40+
.newBuilder()
41+
.setDomain(DOMAIN)
42+
.setShadowMode(Mode.Normal)
43+
.setWorkflowTypes(Lists.newArrayList("GreetingWorkflow::getGreeting"))
44+
.setWorkflowStatuses(Lists.newArrayList(WorkflowStatus.OPEN, WorkflowStatus.CLOSED))
45+
.setExitCondition(new ExitCondition().setExpirationIntervalInSeconds(60))
46+
.build();
47+
WorkflowShadower shadower = new WorkflowShadower(service, options, TASK_LIST);
48+
shadower.registerWorkflowImplementationTypes(HelloActivity.GreetingWorkflowImpl.class);
49+
50+
shadower.run();
51+
}
52+
}

0 commit comments

Comments
 (0)