Hello, in our project we want to check whether is correct the usage of the Workflow.retry() method to call an Activity within it. Furthermore, we would like to understand how does the replay of a workflow actually works when Workflow.retry() is involved. Our initial assumption is that it is the result of the retry method that would be saved in the event log, not the individual calls to the activity within the retry context. Thanks in advance for any help you can provide us.
We have the following example code:
// create RetryOptions for SettingsActivity.getSettings() method
final var retrieveSettingsRetryOptions = RetryOptions.newBuilder()
.setDoNotRetry(SettingsException.class.getName())
.setMaximumAttempts(10)
.setInitialInterval(Duration.ofHours(1))
.setBackoffCoefficient(1.0)
.build();
// Searching settings
final var optSettings = Workflow.retry(retrieveSettingsRetryOptions, Optional.empty(),
// Here we're calling and Activity within the Workflow.retry() to have a distinct
// retry configuration for non SettingsExceptions.
() -> {
try {
settingsActivity.getSettings(request.getAccount())
} catch(SettingsExceptions se) {
eventActivity.publishNotification();
throw se;
}
});
You should use Workflow.retry / Async.retry when you want to retry a group/sequence of activities (which should be retried together). For a single activity you can just add the RetryOption to its ActivityOptions and invoke it directly (without Workflow.retry).
You can look at the history log in the Web UI, with Workflow.retry you still get the ActivityTaskStarter, and ActivityTaskCompleted (with result) events written to the logs, so it is deterministic, if that’s the question. Only difference should be that the service-side retries do not add events to the history on retries.
Thanks Tihomir.
For more context in our use case, the settings Activity can throw two types of exceptions, and for one of those cases we want to wait 1h between retries only for that exceptions ( SlowRetryException ), and a default retry options for the rest of exceptions ( NormalRetryException ). That is the reason why we are using the Wrokflow.retry() method and not just relying only on the Activity’s RetryOptions. Does it make sense to having it that way?
Example code:
@ActivityInterface
public interface SettingsActivity {
@ActivityMethod
@MethodRetry(doNotRetry = {"com...error.SlowRetryException"})
Optional<Model> getSettings(AccountSid accountSid);
}
// Default Retry Options
private static final ActivityOptions DEFAULT_OPTS = ActivityOptions.newBuilder()
.setRetryOptions(RetryOptions.newBuilder()
.setMaximumAttempts(10)
.build())
.build();
this.settingsActivity = Workflow.newActivityStub(SettingsActivity.class, DEFAULT_OPTS);
// RetryOptions for retry after 1h for SlowRetryException errors
final var retrieveSettingsRetryOptions = RetryOptions.newBuilder()
.setDoNotRetry(NormalRetryException.class.getName())
.setMaximumAttempts(10)
.setInitialInterval(Duration.ofHours(1))
.setBackoffCoefficient(1.0)
.build();
final var optSettings = Workflow.retry(retrieveSettingsRetryOptions, Optional.empty(),
() -> {
try {
settingsActivity.getSettings(request.getAccount())
} catch(SlowRetryException se) {
eventActivity.publishNotification();
throw se;
}
});
Furthermore, the settings can change in-between retries since the user can update it at any point during that time. Wouldn’t it be an issue during a replay of the workflow?
Checked internally and your code looks reasonable for the use case. DEFAULT_OPTS is used for all exceptions other than SlowRetryException and Workflow.retry is used for that exception only.