-
Notifications
You must be signed in to change notification settings - Fork 148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
EarlyReturn sample (Update-With-Start) #689
Merged
Quinn-With-Two-Ns
merged 15 commits into
temporalio:main
from
steveandroulakis:earlyreturn
Oct 29, 2024
Merged
Changes from 7 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
284f4fa
EarlyReturn sample (Update-With-Start)
steveandroulakis 5082bb6
multifile but typed stub doesn't work
steveandroulakis d38125b
multifile but typed stub doesn't work
steveandroulakis 86c4883
split files, using jackson for transaction object
steveandroulakis 497f2dc
spotlessApply
steveandroulakis d161cbd
workflow and update returns objects now
steveandroulakis 7700fa1
workflow and update returns objects
steveandroulakis 463c2c7
immutable transaction class
steveandroulakis 78bd0c2
Early Return UpdateWithStart unit test
steveandroulakis b8513d9
Update core/src/main/java/io/temporal/samples/earlyreturn/Transaction…
steveandroulakis 9037387
remove sleep from test
steveandroulakis cb93bfb
Minting has its own activity
steveandroulakis f40e832
Fix nexus sample TLS settings (#688)
Quinn-With-Two-Ns ce6cc56
Add API key option to Nexus sample (#690)
Quinn-With-Two-Ns 8095cc7
Bump Java SDK to v1.26.1 (#695)
Quinn-With-Two-Ns File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
90 changes: 90 additions & 0 deletions
90
core/src/main/java/io/temporal/samples/earlyreturn/EarlyReturnClient.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* | ||
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved | ||
* | ||
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Modifications copyright (C) 2017 Uber Technologies, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). You may not | ||
* use this file except in compliance with the License. A copy of the License is | ||
* located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed on | ||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package io.temporal.samples.earlyreturn; | ||
|
||
import io.temporal.client.*; | ||
import io.temporal.serviceclient.WorkflowServiceStubs; | ||
|
||
public class EarlyReturnClient { | ||
private static final String TASK_QUEUE = "EarlyReturnTaskQueue"; | ||
private static final String WORKFLOW_ID_PREFIX = "early-return-workflow-"; | ||
|
||
public static void main(String[] args) { | ||
WorkflowClient client = setupWorkflowClient(); | ||
runWorkflowWithUpdateWithStart(client); | ||
} | ||
|
||
// Setup the WorkflowClient | ||
public static WorkflowClient setupWorkflowClient() { | ||
WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); | ||
return WorkflowClient.newInstance(service); | ||
} | ||
|
||
// Run workflow using 'updateWithStart' | ||
private static void runWorkflowWithUpdateWithStart(WorkflowClient client) { | ||
Transaction tx = | ||
new Transaction( | ||
"", "Bob", "Alice", | ||
1000); // Change this amount to a negative number to have initTransaction fail | ||
|
||
WorkflowOptions options = buildWorkflowOptions(); | ||
TransactionWorkflow workflow = client.newWorkflowStub(TransactionWorkflow.class, options); | ||
|
||
System.out.println("Starting workflow with UpdateWithStart"); | ||
|
||
UpdateWithStartWorkflowOperation<TxResult> updateOp = | ||
UpdateWithStartWorkflowOperation.newBuilder(workflow::returnInitResult) | ||
.setWaitForStage(WorkflowUpdateStage.COMPLETED) // Wait for update to complete | ||
.build(); | ||
|
||
TxResult updateResult = null; | ||
try { | ||
WorkflowUpdateHandle<TxResult> updateHandle = | ||
WorkflowClient.updateWithStart(workflow::processTransaction, tx, updateOp); | ||
|
||
updateResult = updateHandle.getResultAsync().get(); | ||
|
||
System.out.println( | ||
"Workflow initialized with result: " | ||
+ updateResult.getStatus() | ||
+ " (transactionId: " | ||
+ updateResult.getTransactionId() | ||
+ ")"); | ||
|
||
TxResult result = WorkflowStub.fromTyped(workflow).getResult(TxResult.class); | ||
System.out.println( | ||
"Workflow completed with result: " | ||
+ result.getStatus() | ||
+ " (transactionId: " | ||
+ result.getTransactionId() | ||
+ ")"); | ||
} catch (Exception e) { | ||
System.err.println("Transaction initialization failed: " + e.getMessage()); | ||
} | ||
} | ||
|
||
// Build WorkflowOptions with task queue and unique ID | ||
private static WorkflowOptions buildWorkflowOptions() { | ||
return WorkflowOptions.newBuilder() | ||
.setTaskQueue(TASK_QUEUE) | ||
.setWorkflowId(WORKFLOW_ID_PREFIX + System.currentTimeMillis()) | ||
.build(); | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
core/src/main/java/io/temporal/samples/earlyreturn/EarlyReturnWorker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
/* | ||
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved | ||
* | ||
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Modifications copyright (C) 2017 Uber Technologies, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). You may not | ||
* use this file except in compliance with the License. A copy of the License is | ||
* located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed on | ||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package io.temporal.samples.earlyreturn; | ||
|
||
import io.temporal.client.WorkflowClient; | ||
import io.temporal.worker.Worker; | ||
import io.temporal.worker.WorkerFactory; | ||
|
||
public class EarlyReturnWorker { | ||
private static final String TASK_QUEUE = "EarlyReturnTaskQueue"; | ||
|
||
public static void main(String[] args) { | ||
WorkflowClient client = EarlyReturnClient.setupWorkflowClient(); | ||
startWorker(client); | ||
} | ||
|
||
private static void startWorker(WorkflowClient client) { | ||
WorkerFactory factory = WorkerFactory.newInstance(client); | ||
Worker worker = factory.newWorker(TASK_QUEUE); | ||
|
||
worker.registerWorkflowImplementationTypes(TransactionWorkflowImpl.class); | ||
worker.registerActivitiesImplementations(new TransactionActivitiesImpl()); | ||
|
||
factory.start(); | ||
System.out.println("Worker started"); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
core/src/main/java/io/temporal/samples/earlyreturn/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
### Early-Return Sample | ||
|
||
This sample demonstrates an early-return from a workflow. | ||
|
||
By utilizing Update-with-Start, a client can start a new workflow and synchronously receive | ||
a response mid-workflow, while the workflow continues to run to completion. | ||
|
||
To run the sample, start the worker: | ||
```bash | ||
./gradlew -q execute -PmainClass=io.temporal.samples.earlyreturn.EarlyReturnWorker | ||
``` | ||
|
||
Then, start the client: | ||
|
||
```bash | ||
./gradlew -q execute -PmainClass=io.temporal.samples.earlyreturn.EarlyReturnClient | ||
``` | ||
|
||
* The client will start a workflow using Update-With-Start. | ||
* Update-With-Start will trigger an initialization step. | ||
* 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. | ||
* 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. | ||
|
||
To trigger a failed initialization, set the amount to <= 0 in the `EarlyReturnClient` class's `runWorkflowWithUpdateWithStart` method and re-run the client. |
89 changes: 89 additions & 0 deletions
89
core/src/main/java/io/temporal/samples/earlyreturn/Transaction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
/* | ||
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved | ||
* | ||
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Modifications copyright (C) 2017 Uber Technologies, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). You may not | ||
* use this file except in compliance with the License. A copy of the License is | ||
* located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed on | ||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package io.temporal.samples.earlyreturn; | ||
|
||
import com.fasterxml.jackson.annotation.JsonCreator; | ||
import com.fasterxml.jackson.annotation.JsonProperty; | ||
|
||
public class Transaction { | ||
private String id; // Mutable field | ||
private String sourceAccount; | ||
private String targetAccount; | ||
private int amount; | ||
|
||
// No-arg constructor for serialization frameworks | ||
public Transaction() {} | ||
|
||
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES) | ||
public Transaction( | ||
@JsonProperty("id") String id, | ||
@JsonProperty("sourceAccount") String sourceAccount, | ||
@JsonProperty("targetAccount") String targetAccount, | ||
@JsonProperty("amount") int amount) { | ||
this.id = id; | ||
this.sourceAccount = sourceAccount; | ||
this.targetAccount = targetAccount; | ||
this.amount = amount; | ||
} | ||
|
||
// Getters and Setters for each field | ||
@JsonProperty("id") | ||
public String getId() { | ||
return id; | ||
} | ||
|
||
public void setId(String id) { | ||
this.id = id; | ||
} | ||
|
||
@JsonProperty("sourceAccount") | ||
public String getSourceAccount() { | ||
return sourceAccount; | ||
} | ||
|
||
public void setSourceAccount(String sourceAccount) { | ||
this.sourceAccount = sourceAccount; | ||
} | ||
|
||
@JsonProperty("targetAccount") | ||
public String getTargetAccount() { | ||
return targetAccount; | ||
} | ||
|
||
public void setTargetAccount(String targetAccount) { | ||
this.targetAccount = targetAccount; | ||
} | ||
|
||
@JsonProperty("amount") | ||
public int getAmount() { | ||
return amount; | ||
} | ||
|
||
public void setAmount(int amount) { | ||
this.amount = amount; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return String.format( | ||
"Transaction{id='%s', sourceAccount='%s', targetAccount='%s', amount=%d}", | ||
id, sourceAccount, targetAccount, amount); | ||
} | ||
} |
35 changes: 35 additions & 0 deletions
35
core/src/main/java/io/temporal/samples/earlyreturn/TransactionActivities.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
/* | ||
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved | ||
* | ||
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Modifications copyright (C) 2017 Uber Technologies, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). You may not | ||
* use this file except in compliance with the License. A copy of the License is | ||
* located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed on | ||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package io.temporal.samples.earlyreturn; | ||
|
||
import io.temporal.activity.ActivityInterface; | ||
import io.temporal.activity.ActivityMethod; | ||
|
||
@ActivityInterface | ||
public interface TransactionActivities { | ||
@ActivityMethod | ||
Transaction initTransaction(Transaction tx); | ||
|
||
@ActivityMethod | ||
void cancelTransaction(Transaction tx); | ||
|
||
@ActivityMethod | ||
void completeTransaction(Transaction tx); | ||
} |
67 changes: 67 additions & 0 deletions
67
core/src/main/java/io/temporal/samples/earlyreturn/TransactionActivitiesImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
/* | ||
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved | ||
* | ||
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Modifications copyright (C) 2017 Uber Technologies, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). You may not | ||
* use this file except in compliance with the License. A copy of the License is | ||
* located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed on | ||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package io.temporal.samples.earlyreturn; | ||
|
||
import io.temporal.failure.ApplicationFailure; | ||
|
||
public class TransactionActivitiesImpl implements TransactionActivities { | ||
|
||
@Override | ||
public Transaction initTransaction(Transaction tx) { | ||
System.out.println("Initializing transaction"); | ||
sleep(500); | ||
if (tx.getAmount() <= 0) { | ||
System.out.println("Invalid amount: " + tx.getAmount()); | ||
throw ApplicationFailure.newNonRetryableFailure( | ||
"Non-retryable Activity Failure: Invalid Amount", "InvalidAmount"); | ||
} | ||
tx.setId("TXID" + String.format("%010d", (long) (Math.random() * 1_000_000_0000L))); | ||
sleep(500); | ||
steveandroulakis marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return tx; | ||
} | ||
|
||
@Override | ||
public void cancelTransaction(Transaction tx) { | ||
System.out.println("Cancelling transaction"); | ||
sleep(2000); | ||
System.out.println("Transaction cancelled"); | ||
} | ||
|
||
@Override | ||
public void completeTransaction(Transaction tx) { | ||
System.out.println( | ||
"Sending $" | ||
+ tx.getAmount() | ||
+ " from " | ||
+ tx.getSourceAccount() | ||
+ " to " | ||
+ tx.getTargetAccount()); | ||
sleep(2000); | ||
System.out.println("Transaction completed successfully"); | ||
} | ||
|
||
private void sleep(long millis) { | ||
try { | ||
Thread.sleep(millis); | ||
} catch (InterruptedException e) { | ||
Thread.currentThread().interrupt(); | ||
} | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
core/src/main/java/io/temporal/samples/earlyreturn/TransactionWorkflow.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
/* | ||
* Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved | ||
* | ||
* Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved. | ||
* | ||
* Modifications copyright (C) 2017 Uber Technologies, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"). You may not | ||
* use this file except in compliance with the License. A copy of the License is | ||
* located at | ||
* | ||
* http://aws.amazon.com/apache2.0 | ||
* | ||
* or in the "license" file accompanying this file. This file is distributed on | ||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either | ||
* express or implied. See the License for the specific language governing | ||
* permissions and limitations under the License. | ||
*/ | ||
|
||
package io.temporal.samples.earlyreturn; | ||
|
||
import io.temporal.workflow.UpdateMethod; | ||
import io.temporal.workflow.WorkflowInterface; | ||
import io.temporal.workflow.WorkflowMethod; | ||
|
||
@WorkflowInterface | ||
public interface TransactionWorkflow { | ||
@WorkflowMethod | ||
TxResult processTransaction(Transaction tx); | ||
|
||
@UpdateMethod(name = "early-return") | ||
TxResult returnInitResult(); | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the Go example, I'm generated the transaction ID on the client and use it in the Workflow ID. That way, the user would receive an error when they start a workflow for the same transaction again. I don't think it's strictly necessary to mimic that here, but I thought I'd mention it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gotcha. I intentionally changed it so the early-return would contain meaningful data usable for the rest of the transaction (an early-return / initialization step that returns null isn't very illustrative of real-world value imo)
I plan on following Quinn's suggestion here of still having the ID minted as part of an activity, but doing it in a separate one as part of the initialization step #689 (comment) -- Let me know if there's anything you'd do differently