Skip to content

Commit 9cdf420

Browse files
authored
Merge pull request #49 from uber/shadowing_sample
Add shadowing samples
2 parents 7589265 + 5edad66 commit 9cdf420

File tree

6 files changed

+155
-1
lines changed

6 files changed

+155
-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: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
##Shadowing example:
2+
3+
We provide Workflow Shadowing as a local testing solution. The example can be found in
4+
[HelloWorkflowShadowingTest](../../../../../../../test/java/com/uber/cadence/samples/hello/HelloWorkflowShadowingTest.java)
5+
6+
7+
This shadowing worker uses for detecting workflow non-deterministic error
8+
prior to the workflow code deployment to prod. You can setup this shadowing worker in non-prod envrionment to shadow production traffic.
9+
10+
More detail can be found: [design doc](https://github.com/uber/cadence/blob/master/docs/design/workflow-shadowing/2547-workflow-shadowing.md)
11+
12+
1. To run this example, start a 0.21+ cadence server.
13+
14+
2. Run a few HelloActivity workflow to generate workflow records.
15+
```
16+
./gradlew -q execute -PmainClass=com.uber.cadence.samples.hello.HelloActivity
17+
```
18+
19+
3. Run the traffic shadowing
20+
```
21+
./gradlew -q execute -PmainClass=com.uber.cadence.samples.shadowing.ShadowTraffic
22+
```
23+
24+
4. No non-deterministic error is expected in the stdout.
25+
26+
5. Add a non backward compatible change to HelloActivity.
27+
```
28+
for example: add a timer between workflow start and activity schedule
29+
30+
Workflow.sleep(1000);
31+
32+
```
33+
34+
6. Run the traffic shadowing
35+
```
36+
./gradlew -q execute -PmainClass=com.uber.cadence.samples.shadowing.ShadowTraffic
37+
```
38+
39+
7. Non-deterministic error is expected in the stdout.
40+
41+
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+
}

src/test/resources/history.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[{"eventId":1,"timestamp":1615406829683961000,"eventType":"WorkflowExecutionStarted","version":-24,"taskId":1048576,"workflowExecutionStartedEventAttributes":{"workflowType":{"name":"FileProcessingWorkflow::processFile"},"taskList":{"name":"FileProcessing"},"input":"WyJodHRwOi8vd3d3Lmdvb2dsZS5jb20vIiwiaHR0cDovL2R1bW15Il0=","executionStartToCloseTimeoutSeconds":30,"taskStartToCloseTimeoutSeconds":10,"continuedExecutionRunId":"","originalExecutionRunId":"a3073e45-106d-4463-b1ae-1516070e0136","identity":"","firstExecutionRunId":"a3073e45-106d-4463-b1ae-1516070e0136","attempt":0,"cronSchedule":"","firstDecisionTaskBackoffSeconds":0}},{"eventId":2,"timestamp":1615406829683990000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":1048577,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"FileProcessing"},"startToCloseTimeoutSeconds":10,"attempt":0}},{"eventId":3,"timestamp":1615406829704269000,"eventType":"DecisionTaskStarted","version":-24,"taskId":1048582,"decisionTaskStartedEventAttributes":{"scheduledEventId":2,"identity":"14493@yx-C02ZD32HLVDQ","requestId":"4a5d6a33-d6f7-469e-a040-1d20004f0aaf"}},{"eventId":4,"timestamp":1615406829872782000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":1048585,"decisionTaskCompletedEventAttributes":{"scheduledEventId":2,"startedEventId":3,"identity":"14493@yx-C02ZD32HLVDQ","binaryChecksum":""}},{"eventId":5,"timestamp":1615406829872867000,"eventType":"MarkerRecorded","version":-24,"taskId":1048586,"markerRecordedEventAttributes":{"markerName":"MutableSideEffect","details":"eyJpbml0aWFsSW50ZXJ2YWwiOnsic2Vjb25kcyI6MSwibmFub3MiOjB9LCJiYWNrb2ZmQ29lZmZpY2llbnQiOjIuMCwiZXhwaXJhdGlvbiI6eyJzZWNvbmRzIjoxMCwibmFub3MiOjB9LCJtYXhpbXVtQXR0ZW1wdHMiOjAsIm1heGltdW1JbnRlcnZhbCI6bnVsbCwiZG9Ob3RSZXRyeSI6bnVsbH0=","decisionTaskCompletedEventId":4,"header":{"fields":{"MutableMarkerHeader":"eyJpZCI6IjBmYjhhZmNjLWNmYTUtMzFhZi1iZDhiLTI1OGUwMTcxZTU2MyIsImV2ZW50SWQiOjUsImFjY2Vzc0NvdW50IjowfQ=="}}}},{"eventId":6,"timestamp":1615406829872878000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":1048587,"activityTaskScheduledEventAttributes":{"activityId":"1","activityType":{"name":"StoreActivities::download"},"domain":"","taskList":{"name":"FileProcessing"},"input":"Imh0dHA6Ly93d3cuZ29vZ2xlLmNvbS8i","scheduleToCloseTimeoutSeconds":10,"scheduleToStartTimeoutSeconds":10,"startToCloseTimeoutSeconds":10,"heartbeatTimeoutSeconds":10,"decisionTaskCompletedEventId":4}},{"eventId":7,"timestamp":1615406829881959000,"eventType":"ActivityTaskStarted","version":-24,"taskId":1048591,"activityTaskStartedEventAttributes":{"scheduledEventId":6,"identity":"14493@yx-C02ZD32HLVDQ","requestId":"6a837403-a3c5-4576-931f-a4d181f9b7f1","attempt":0,"lastFailureReason":""}},{"eventId":8,"timestamp":1615406830028265000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":1048594,"activityTaskCompletedEventAttributes":{"result":"eyJob3N0VGFza0xpc3QiOiIxNDQ5M0B5eC1DMDJaRDMySExWRFEiLCJmaWxlTmFtZSI6Ii92YXIvZm9sZGVycy9fbi8zYnFiemR2ZDMwZDFnNjJqcV8xeDh5MTQwMDAwZ24vVC8xNjE1NDA2ODMwMDE4LTAvZG93bmxvYWRlZCJ9","scheduledEventId":6,"startedEventId":7,"identity":"14493@yx-C02ZD32HLVDQ"}},{"eventId":9,"timestamp":1615406830028320000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":1048596,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"yx-C02ZD32HLVDQ:6a756b8f-9140-48de-8381-3fb0787127d2"},"startToCloseTimeoutSeconds":10,"attempt":0}},{"eventId":10,"timestamp":1615406830035061000,"eventType":"DecisionTaskStarted","version":-24,"taskId":1048600,"decisionTaskStartedEventAttributes":{"scheduledEventId":9,"identity":"6a756b8f-9140-48de-8381-3fb0787127d2","requestId":"9c3fc049-d99a-4b4c-8d78-8bb37260d410"}},{"eventId":11,"timestamp":1615406830057168000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":1048603,"decisionTaskCompletedEventAttributes":{"scheduledEventId":9,"startedEventId":10,"identity":"14493@yx-C02ZD32HLVDQ","binaryChecksum":""}},{"eventId":12,"timestamp":1615406830057191000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":1048604,"activityTaskScheduledEventAttributes":{"activityId":"2","activityType":{"name":"StoreActivities::process"},"domain":"","taskList":{"name":"14493@yx-C02ZD32HLVDQ"},"input":"Ii92YXIvZm9sZGVycy9fbi8zYnFiemR2ZDMwZDFnNjJqcV8xeDh5MTQwMDAwZ24vVC8xNjE1NDA2ODMwMDE4LTAvZG93bmxvYWRlZCI=","scheduleToCloseTimeoutSeconds":10,"scheduleToStartTimeoutSeconds":10,"startToCloseTimeoutSeconds":10,"heartbeatTimeoutSeconds":10,"decisionTaskCompletedEventId":11}},{"eventId":13,"timestamp":1615406830062863000,"eventType":"ActivityTaskStarted","version":-24,"taskId":1048608,"activityTaskStartedEventAttributes":{"scheduledEventId":12,"identity":"14493@yx-C02ZD32HLVDQ","requestId":"fc07913a-32dd-42dd-97f2-0c4bba882546","attempt":0,"lastFailureReason":""}},{"eventId":14,"timestamp":1615406830071599000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":1048611,"activityTaskCompletedEventAttributes":{"result":"Ii92YXIvZm9sZGVycy9fbi8zYnFiemR2ZDMwZDFnNjJqcV8xeDh5MTQwMDAwZ24vVC8xNjE1NDA2ODMwMDE4LTAvcHJvY2Vzc2VkIg==","scheduledEventId":12,"startedEventId":13,"identity":"14493@yx-C02ZD32HLVDQ"}},{"eventId":15,"timestamp":1615406830071609000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":1048613,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"yx-C02ZD32HLVDQ:6a756b8f-9140-48de-8381-3fb0787127d2"},"startToCloseTimeoutSeconds":10,"attempt":0}},{"eventId":16,"timestamp":1615406830077115000,"eventType":"DecisionTaskStarted","version":-24,"taskId":1048617,"decisionTaskStartedEventAttributes":{"scheduledEventId":15,"identity":"6a756b8f-9140-48de-8381-3fb0787127d2","requestId":"8b60aa4a-beac-47ee-95ab-7f6a24139a96"}},{"eventId":17,"timestamp":1615406830085797000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":1048620,"decisionTaskCompletedEventAttributes":{"scheduledEventId":15,"startedEventId":16,"identity":"14493@yx-C02ZD32HLVDQ","binaryChecksum":""}},{"eventId":18,"timestamp":1615406830085819000,"eventType":"ActivityTaskScheduled","version":-24,"taskId":1048621,"activityTaskScheduledEventAttributes":{"activityId":"3","activityType":{"name":"StoreActivities::upload"},"domain":"","taskList":{"name":"14493@yx-C02ZD32HLVDQ"},"input":"WyIvdmFyL2ZvbGRlcnMvX24vM2JxYnpkdmQzMGQxZzYyanFfMXg4eTE0MDAwMGduL1QvMTYxNTQwNjgzMDAxOC0wL3Byb2Nlc3NlZCIsImh0dHA6Ly9kdW1teSJd","scheduleToCloseTimeoutSeconds":10,"scheduleToStartTimeoutSeconds":10,"startToCloseTimeoutSeconds":10,"heartbeatTimeoutSeconds":10,"decisionTaskCompletedEventId":17}},{"eventId":19,"timestamp":1615406830091214000,"eventType":"ActivityTaskStarted","version":-24,"taskId":1048625,"activityTaskStartedEventAttributes":{"scheduledEventId":18,"identity":"14493@yx-C02ZD32HLVDQ","requestId":"5994a514-8fdc-4a20-9133-f32ad8b40800","attempt":0,"lastFailureReason":""}},{"eventId":20,"timestamp":1615406830097800000,"eventType":"ActivityTaskCompleted","version":-24,"taskId":1048628,"activityTaskCompletedEventAttributes":{"scheduledEventId":18,"startedEventId":19,"identity":"14493@yx-C02ZD32HLVDQ"}},{"eventId":21,"timestamp":1615406830097810000,"eventType":"DecisionTaskScheduled","version":-24,"taskId":1048630,"decisionTaskScheduledEventAttributes":{"taskList":{"name":"yx-C02ZD32HLVDQ:6a756b8f-9140-48de-8381-3fb0787127d2"},"startToCloseTimeoutSeconds":10,"attempt":0}},{"eventId":22,"timestamp":1615406830103719000,"eventType":"DecisionTaskStarted","version":-24,"taskId":1048634,"decisionTaskStartedEventAttributes":{"scheduledEventId":21,"identity":"6a756b8f-9140-48de-8381-3fb0787127d2","requestId":"d70fc76c-a921-4b68-83d9-f87ed1bbf9fb"}},{"eventId":23,"timestamp":1615406830115206000,"eventType":"DecisionTaskCompleted","version":-24,"taskId":1048637,"decisionTaskCompletedEventAttributes":{"scheduledEventId":21,"startedEventId":22,"identity":"14493@yx-C02ZD32HLVDQ","binaryChecksum":""}},{"eventId":24,"timestamp":1615406830115238000,"eventType":"WorkflowExecutionCompleted","version":-24,"taskId":1048638,"workflowExecutionCompletedEventAttributes":{"decisionTaskCompletedEventId":23}}]

0 commit comments

Comments
 (0)