Skip to content
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
merged 15 commits into from
Oct 29, 2024

Conversation

steveandroulakis
Copy link
Contributor

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

  1. Closes
    No issue

  2. How was this tested:
    I welcome guidance on how to effectively write tests for this!

  3. Any docs updates needed?
    No

@CLAassistant
Copy link

CLAassistant commented Oct 18, 2024

CLA assistant check
All committers have signed the CLA.

@mfateev
Copy link
Member

mfateev commented Oct 18, 2024

We should either use the proper Java style of separating classes and interfaces into their own files or implement this as another Hello sample.

@steveandroulakis
Copy link
Contributor Author

We should either use the proper Java style of separating classes and interfaces into their own files or implement this as another Hello sample.

Thanks, will split into separate files.

Copy link

@drewhoskins-temporal drewhoskins-temporal left a 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() {

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.

Copy link
Contributor Author

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;
Copy link
Contributor

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

Copy link
Contributor Author

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

@Quinn-With-Two-Ns
Copy link
Contributor

@stephanos May want to take a look

@Quinn-With-Two-Ns
Copy link
Contributor

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.

@steveandroulakis
Copy link
Contributor Author

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) {
Copy link
Contributor

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?

Copy link
Contributor Author

@steveandroulakis steveandroulakis Oct 25, 2024

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

  1. InitTransaction cause an activity failure (fundamental to @stephanos original sample) AND
  2. 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!

Copy link
Contributor

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

private static WorkflowOptions buildWorkflowOptions() {
return WorkflowOptions.newBuilder()
.setTaskQueue(TASK_QUEUE)
.setWorkflowId(WORKFLOW_ID_PREFIX + System.currentTimeMillis())
Copy link
Contributor

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.

Copy link
Contributor Author

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));
Copy link
Contributor

@stephanos stephanos Oct 25, 2024

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.

Copy link
Contributor Author

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

@Quinn-With-Two-Ns Quinn-With-Two-Ns merged commit 58124a8 into temporalio:main Oct 29, 2024
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants