-
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
EarlyReturn sample (Update-With-Start) #689
Conversation
We should either use the proper Java style of separating classes and interfaces into their own files or implement this as another Hello sample. |
core/src/main/java/io/temporal/samples/earlyreturn/EarlyReturn.java
Outdated
Show resolved
Hide resolved
core/src/main/java/io/temporal/samples/earlyreturn/EarlyReturn.java
Outdated
Show resolved
Hide resolved
Thanks, will split into separate files. |
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.
optional nit
} | ||
|
||
@Override | ||
public String returnInitResult() { |
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.
nit: We generally try to set a good example and return structures rather than scalars for future compat of being able to add new fields. I was asked to improve a similar situation when I wrote a python sample, so figured I'd echo the advice here. FF to ignore since I'm not sure how consistent we really are and understand it's orthogonal.
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.
Done
|
||
@Override | ||
public TxResult processTransaction(Transaction txInput) { | ||
this.tx = txInput; |
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.
I would refactor this sample to make Transaction
immutable and not modify transaction as the workflow progress. I would make two different classes one for the workflow input that doesn't have a transaction ID field and one (maybe returned by the activity initTransaction
or created in the workflow) that the cancelTransaction
and completeTransaction
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.
Done: creation of a TransactionRequest object + the Transaction class is now immutable
core/src/main/java/io/temporal/samples/earlyreturn/TransactionWorkflowImpl.java
Show resolved
Hide resolved
@stephanos May want to take a look |
A unit test showing update with start would be great, @stephanos but a lot of work into the time skipping test server part, but I wouldn't block the PR on it if you don't have time. |
I have written 2 unit tests. I hope they're done correctly! |
} | ||
|
||
@Override | ||
public void cancelTransaction(TransactionRequest txRequest) { |
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.
I think cancelTransaction
should also take the Transaction
class since, in practice, you would need the transaction ID to cancel it right?
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.
I'm diverging more and more from the original Go sample (which is fine).
Right now, I think of an initialization as something that results in the 'minting' of a Transaction ID. And a failure of initialization causes an activity failure which triggers a cancellation.
I can't think of a way of having
- InitTransaction cause an activity failure (fundamental to @stephanos original sample) AND
- Returning a transaction ID that would mean the creation of a Transaction object I can then use in cancelTransaction
Thinking blank-slate for a moment, and about the purpose of an "Early Return" type sample. @Quinn-With-Two-Ns -- What do you think you would want to see from a scenario where a transaction fails on initialization (e.g. the workflow update with start)? What data should be returned and what compensation should take place after? I don't think I should spend any more hours chewing on this sample without figuring that out first. Thanks!
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.
Yeah you raise a great point. I think I would separate the creation of the ID from the initialization
of the transaction. Treating the transaction ID as an idempotency key for the rest of the activities. That being said I think this sample is really good as is and an improvement over the Go Sample so
wouldn't hold up the PR on this
core/src/main/java/io/temporal/samples/earlyreturn/TransactionActivities.java
Outdated
Show resolved
Hide resolved
core/src/main/java/io/temporal/samples/earlyreturn/TransactionActivitiesImpl.java
Show resolved
Hide resolved
…Activities.java Co-authored-by: Stephan Behnke <[email protected]>
private static WorkflowOptions buildWorkflowOptions() { | ||
return WorkflowOptions.newBuilder() | ||
.setTaskQueue(TASK_QUEUE) | ||
.setWorkflowId(WORKFLOW_ID_PREFIX + System.currentTimeMillis()) |
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
assertTrue(appFailure.getMessage().contains("Invalid Amount")); | ||
|
||
// Let workflow process error handling | ||
testWorkflowRule.getTestEnvironment().sleep(Duration.ofSeconds(1)); |
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.
Out of curiosity, is this sleep necessary? I would have expected the getResult
further down would just block until the result is available.
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.
You're absolutely right. There's no need as getResult blocks for a response. Removing
If it is a significant code change, please make sure there is an open issue for this.
We work best with you when we have accepted the idea first before you code. -->
What was changed
Sample that uses the new Update-With-Start feature.
Why?
Help users understand what Update-With-Start can do with a simple financial transaction example.
Based on the Go sample here: https://github.com/temporalio/samples-go/tree/main/early-return
Closes
No issue
How was this tested:
I welcome guidance on how to effectively write tests for this!
Any docs updates needed?
No