Skip to content

Commit 58124a8

Browse files
steveandroulakisstephanosQuinn-With-Two-Ns
authored
EarlyReturn sample (Update-With-Start) (#689)
EarlyReturn sample (Update-With-Start) --------- Co-authored-by: Stephan Behnke <[email protected]> Co-authored-by: Quinn Klassen <[email protected]>
1 parent 71e251d commit 58124a8

File tree

11 files changed

+717
-0
lines changed

11 files changed

+717
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
3+
*
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
9+
* use this file except in compliance with the License. A copy of the License is
10+
* located at
11+
*
12+
* http://aws.amazon.com/apache2.0
13+
*
14+
* or in the "license" file accompanying this file. This file is distributed on
15+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
16+
* express or implied. See the License for the specific language governing
17+
* permissions and limitations under the License.
18+
*/
19+
20+
package io.temporal.samples.earlyreturn;
21+
22+
import io.temporal.client.*;
23+
import io.temporal.serviceclient.WorkflowServiceStubs;
24+
25+
public class EarlyReturnClient {
26+
private static final String TASK_QUEUE = "EarlyReturnTaskQueue";
27+
private static final String WORKFLOW_ID_PREFIX = "early-return-workflow-";
28+
29+
public static void main(String[] args) {
30+
WorkflowClient client = setupWorkflowClient();
31+
runWorkflowWithUpdateWithStart(client);
32+
}
33+
34+
// Set up the WorkflowClient
35+
public static WorkflowClient setupWorkflowClient() {
36+
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs();
37+
return WorkflowClient.newInstance(service);
38+
}
39+
40+
// Run workflow using 'updateWithStart'
41+
private static void runWorkflowWithUpdateWithStart(WorkflowClient client) {
42+
TransactionRequest txRequest =
43+
new TransactionRequest(
44+
"Bob", "Alice",
45+
1000); // Change this amount to a negative number to have initTransaction fail
46+
47+
WorkflowOptions options = buildWorkflowOptions();
48+
TransactionWorkflow workflow = client.newWorkflowStub(TransactionWorkflow.class, options);
49+
50+
System.out.println("Starting workflow with UpdateWithStart");
51+
52+
UpdateWithStartWorkflowOperation<TxResult> updateOp =
53+
UpdateWithStartWorkflowOperation.newBuilder(workflow::returnInitResult)
54+
.setWaitForStage(WorkflowUpdateStage.COMPLETED) // Wait for update to complete
55+
.build();
56+
57+
TxResult updateResult = null;
58+
try {
59+
WorkflowUpdateHandle<TxResult> updateHandle =
60+
WorkflowClient.updateWithStart(workflow::processTransaction, txRequest, updateOp);
61+
62+
updateResult = updateHandle.getResultAsync().get();
63+
64+
System.out.println(
65+
"Workflow initialized with result: "
66+
+ updateResult.getStatus()
67+
+ " (transactionId: "
68+
+ updateResult.getTransactionId()
69+
+ ")");
70+
71+
TxResult result = WorkflowStub.fromTyped(workflow).getResult(TxResult.class);
72+
System.out.println(
73+
"Workflow completed with result: "
74+
+ result.getStatus()
75+
+ " (transactionId: "
76+
+ result.getTransactionId()
77+
+ ")");
78+
} catch (Exception e) {
79+
System.err.println("Transaction initialization failed: " + e.getMessage());
80+
}
81+
}
82+
83+
// Build WorkflowOptions with task queue and unique ID
84+
private static WorkflowOptions buildWorkflowOptions() {
85+
return WorkflowOptions.newBuilder()
86+
.setTaskQueue(TASK_QUEUE)
87+
.setWorkflowId(WORKFLOW_ID_PREFIX + System.currentTimeMillis())
88+
.build();
89+
}
90+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
3+
*
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
9+
* use this file except in compliance with the License. A copy of the License is
10+
* located at
11+
*
12+
* http://aws.amazon.com/apache2.0
13+
*
14+
* or in the "license" file accompanying this file. This file is distributed on
15+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
16+
* express or implied. See the License for the specific language governing
17+
* permissions and limitations under the License.
18+
*/
19+
20+
package io.temporal.samples.earlyreturn;
21+
22+
import io.temporal.client.WorkflowClient;
23+
import io.temporal.worker.Worker;
24+
import io.temporal.worker.WorkerFactory;
25+
26+
public class EarlyReturnWorker {
27+
private static final String TASK_QUEUE = "EarlyReturnTaskQueue";
28+
29+
public static void main(String[] args) {
30+
WorkflowClient client = EarlyReturnClient.setupWorkflowClient();
31+
startWorker(client);
32+
}
33+
34+
private static void startWorker(WorkflowClient client) {
35+
WorkerFactory factory = WorkerFactory.newInstance(client);
36+
Worker worker = factory.newWorker(TASK_QUEUE);
37+
38+
worker.registerWorkflowImplementationTypes(TransactionWorkflowImpl.class);
39+
worker.registerActivitiesImplementations(new TransactionActivitiesImpl());
40+
41+
factory.start();
42+
System.out.println("Worker started");
43+
}
44+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
### Early-Return Sample
2+
3+
This sample demonstrates an early-return from a workflow.
4+
5+
By utilizing Update-with-Start, a client can start a new workflow and synchronously receive
6+
a response mid-workflow, while the workflow continues to run to completion.
7+
8+
To run the sample, start the worker:
9+
```bash
10+
./gradlew -q execute -PmainClass=io.temporal.samples.earlyreturn.EarlyReturnWorker
11+
```
12+
13+
Then, start the client:
14+
15+
```bash
16+
./gradlew -q execute -PmainClass=io.temporal.samples.earlyreturn.EarlyReturnClient
17+
```
18+
19+
* The client will start a workflow using Update-With-Start.
20+
* Update-With-Start will trigger an initialization step.
21+
* If the initialization step succeeds (default), intialization will return to the client with a transaction ID and the workflow will continue. The workflow will then complete and return the final result.
22+
* If the intitialization step fails (amount <= 0), the workflow will return to the client with an error message and the workflow will run an activity to cancel the transaction.
23+
24+
To trigger a failed initialization, set the amount to <= 0 in the `EarlyReturnClient` class's `runWorkflowWithUpdateWithStart` method and re-run the client.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
3+
*
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
9+
* use this file except in compliance with the License. A copy of the License is
10+
* located at
11+
*
12+
* http://aws.amazon.com/apache2.0
13+
*
14+
* or in the "license" file accompanying this file. This file is distributed on
15+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
16+
* express or implied. See the License for the specific language governing
17+
* permissions and limitations under the License.
18+
*/
19+
20+
package io.temporal.samples.earlyreturn;
21+
22+
import com.fasterxml.jackson.annotation.JsonCreator;
23+
import com.fasterxml.jackson.annotation.JsonProperty;
24+
25+
public final class Transaction {
26+
private final String id;
27+
private final String sourceAccount;
28+
private final String targetAccount;
29+
private final int amount;
30+
31+
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
32+
public Transaction(
33+
@JsonProperty("id") String id,
34+
@JsonProperty("sourceAccount") String sourceAccount,
35+
@JsonProperty("targetAccount") String targetAccount,
36+
@JsonProperty("amount") int amount) {
37+
this.id = id;
38+
this.sourceAccount = sourceAccount;
39+
this.targetAccount = targetAccount;
40+
this.amount = amount;
41+
}
42+
43+
@JsonProperty("id")
44+
public String getId() {
45+
return id;
46+
}
47+
48+
@JsonProperty("sourceAccount")
49+
public String getSourceAccount() {
50+
return sourceAccount;
51+
}
52+
53+
@JsonProperty("targetAccount")
54+
public String getTargetAccount() {
55+
return targetAccount;
56+
}
57+
58+
@JsonProperty("amount")
59+
public int getAmount() {
60+
return amount;
61+
}
62+
63+
@Override
64+
public String toString() {
65+
return String.format(
66+
"Transaction{id='%s', sourceAccount='%s', targetAccount='%s', amount=%d}",
67+
id, sourceAccount, targetAccount, amount);
68+
}
69+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
3+
*
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
9+
* use this file except in compliance with the License. A copy of the License is
10+
* located at
11+
*
12+
* http://aws.amazon.com/apache2.0
13+
*
14+
* or in the "license" file accompanying this file. This file is distributed on
15+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
16+
* express or implied. See the License for the specific language governing
17+
* permissions and limitations under the License.
18+
*/
19+
20+
package io.temporal.samples.earlyreturn;
21+
22+
import io.temporal.activity.ActivityInterface;
23+
import io.temporal.activity.ActivityMethod;
24+
25+
@ActivityInterface
26+
public interface TransactionActivities {
27+
@ActivityMethod
28+
Transaction mintTransactionId(TransactionRequest txRequest);
29+
30+
@ActivityMethod
31+
Transaction initTransaction(Transaction tx);
32+
33+
@ActivityMethod
34+
void cancelTransaction(Transaction tx);
35+
36+
@ActivityMethod
37+
void completeTransaction(Transaction tx);
38+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/*
2+
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved
3+
*
4+
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.
5+
*
6+
* Modifications copyright (C) 2017 Uber Technologies, Inc.
7+
*
8+
* Licensed under the Apache License, Version 2.0 (the "License"). You may not
9+
* use this file except in compliance with the License. A copy of the License is
10+
* located at
11+
*
12+
* http://aws.amazon.com/apache2.0
13+
*
14+
* or in the "license" file accompanying this file. This file is distributed on
15+
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
16+
* express or implied. See the License for the specific language governing
17+
* permissions and limitations under the License.
18+
*/
19+
20+
package io.temporal.samples.earlyreturn;
21+
22+
import io.temporal.failure.ApplicationFailure;
23+
24+
public class TransactionActivitiesImpl implements TransactionActivities {
25+
26+
@Override
27+
public Transaction mintTransactionId(TransactionRequest request) {
28+
System.out.println("Minting transaction ID");
29+
// Simulate transaction ID generation
30+
String txId = "TXID" + String.format("%010d", (long) (Math.random() * 1_000_000_0000L));
31+
sleep(100);
32+
System.out.println("Transaction ID minted: " + txId);
33+
return new Transaction(
34+
txId, request.getSourceAccount(), request.getTargetAccount(), request.getAmount());
35+
}
36+
37+
@Override
38+
public Transaction initTransaction(Transaction tx) {
39+
System.out.println("Initializing transaction");
40+
sleep(300);
41+
if (tx.getAmount() <= 0) {
42+
System.out.println("Invalid amount: " + tx.getAmount());
43+
throw ApplicationFailure.newNonRetryableFailure(
44+
"Non-retryable Activity Failure: Invalid Amount", "InvalidAmount");
45+
}
46+
47+
sleep(500);
48+
return tx;
49+
}
50+
51+
@Override
52+
public void cancelTransaction(Transaction tx) {
53+
System.out.println("Cancelling transaction");
54+
sleep(300);
55+
System.out.println("Transaction cancelled");
56+
}
57+
58+
@Override
59+
public void completeTransaction(Transaction tx) {
60+
System.out.println(
61+
"Sending $"
62+
+ tx.getAmount()
63+
+ " from "
64+
+ tx.getSourceAccount()
65+
+ " to "
66+
+ tx.getTargetAccount());
67+
sleep(2000);
68+
System.out.println("Transaction completed successfully");
69+
}
70+
71+
private void sleep(long millis) {
72+
try {
73+
Thread.sleep(millis);
74+
} catch (InterruptedException e) {
75+
Thread.currentThread().interrupt();
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)