Skip to content

Commit 712bae9

Browse files
IWF-405: rename and validate failure recovery policy (#278)
1 parent 31d3b31 commit 712bae9

File tree

5 files changed

+123
-12
lines changed

5 files changed

+123
-12
lines changed

src/main/java/io/iworkflow/core/Client.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public String startWorkflow(
172172
throw new WorkflowDefinitionException(String.format("input cannot be assigned to the starting state, input type: %s, starting state input type: %s", input.getClass(), registeredInputType));
173173
}
174174

175-
WorkflowStateOptions stateOptions = stateDef.getWorkflowState().getStateOptions();
175+
WorkflowStateOptions stateOptions = StateMovementMapper.validateAndGetStateOptions(stateDef);
176176
if (shouldSkipWaitUntil(stateDef.getWorkflowState())) {
177177
if (stateOptions == null) {
178178
stateOptions = new WorkflowStateOptions().skipWaitUntil(true);
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,99 @@
11
package io.iworkflow.core;
22

33
import io.iworkflow.gen.models.ExecuteApiFailurePolicy;
4+
import io.iworkflow.gen.models.WaitUntilApiFailurePolicy;
45
import io.iworkflow.gen.models.WorkflowStateOptions;
56

6-
// WorkflowStateOptionsExtension provides extension to WorkflowStateOptions
7-
// to make it easier to build
7+
/**
8+
* WorkflowStateOptionsExtension provides extension to WorkflowStateOptions
9+
* to make it easier to build some fields of the stateOptions.
10+
* This is also because WorkflowState interface uses WorkflowStateOptions
11+
* directly instead of using a separate model.
12+
* See <a href="https://github.com/indeedeng/iwf-java-sdk/issues/200">TODO</a>
13+
* Example usage in a state implementation:
14+
* public WorkflowStateOptions getStateOptions() {
15+
* return new WorkflowStateOptionsExtension()
16+
* .setProceedAfterRetryExhaustedOnExecute(StateRecoverBasic.class)
17+
* .executeApiRetryPolicy(
18+
* new RetryPolicy()
19+
* .maximumAttempts(10)
20+
* );
21+
* }
22+
*/
823
public class WorkflowStateOptionsExtension extends WorkflowStateOptions {
924

25+
/**
26+
* By default, workflow would fail after execute API retry exhausted.
27+
* Set the state to proceed to the specified state after the execute API exhausted all retries
28+
* This is useful for some advanced use cases like SAGA pattern.
29+
* RetryPolicy is required to be set with maximumAttempts or maximumAttemptsDurationSeconds for execute API.
30+
* NOTE: The proceeding state will take the same input as the failed state that proceeded from.
31+
* See more in <a href="https://github.com/indeedeng/iwf/wiki/WorkflowStateOptions">wiki</a>
32+
* @param proceedingState the state to proceed to
33+
* @return this
34+
*/
35+
public WorkflowStateOptionsExtension setProceedWhenExecuteRetryExhausted(
36+
final Class<? extends WorkflowState> proceedingState) {
37+
return this.setProceedOnExecuteFailure(proceedingState);
38+
}
39+
40+
/**
41+
* By default, workflow would fail after execute API retry exhausted.
42+
* Set the state to proceed to the specified state after the execute API exhausted all retries
43+
* This is useful for some advanced use cases like SAGA pattern.
44+
* RetryPolicy is required to be set with maximumAttempts or maximumAttemptsDurationSeconds for execute API.
45+
* NOTE: The proceeding state will take the same input as the failed state that proceeded from.
46+
* See more in <a href="https://github.com/indeedeng/iwf/wiki/WorkflowStateOptions">wiki</a>
47+
* @param proceedingState the state to proceed to
48+
* @param stateOptionsOverride the stateOptions for the proceeding state. This is for a rare case that you
49+
* need to override the stateOptions returned from state instance.
50+
* @return this
51+
*/
52+
public WorkflowStateOptionsExtension setProceedWhenExecuteRetryExhausted(
53+
final Class<? extends WorkflowState> proceedingState, WorkflowStateOptions stateOptionsOverride) {
54+
return this.setProceedOnExecuteFailure(proceedingState, stateOptionsOverride);
55+
}
56+
57+
/**
58+
* By default, workflow would fail after waitUntil API retry exhausted.
59+
* If set to true, then after waitUntil API exhausted all retries, proceed to the execute API
60+
* This is useful for some advanced use cases like SAGA pattern.
61+
* RetryPolicy is required to be set with maximumAttempts or maximumAttemptsDurationSeconds for waitUntil API.
62+
* NOTE: execute API will use commandResults to check whether the waitUntil has succeeded or not.
63+
* See more in <a href="https://github.com/indeedeng/iwf/wiki/WorkflowStateOptions">wiki</a>
64+
* @param proceed true to proceed
65+
* @return this
66+
*/
67+
public WorkflowStateOptionsExtension setProceedWhenWaitUntilRetryExhausted(boolean proceed){
68+
if(proceed){
69+
this.waitUntilApiFailurePolicy(WaitUntilApiFailurePolicy.PROCEED_ON_FAILURE);
70+
}else{
71+
this.waitUntilApiFailurePolicy(WaitUntilApiFailurePolicy.FAIL_WORKFLOW_ON_FAILURE);
72+
}
73+
74+
return this;
75+
}
76+
77+
/**
78+
* Use setProceedAfterRetryExhaustedOnExecuteFailure instead.
79+
* It's a renaming for better clarity.
80+
*/
81+
@Deprecated
1082
public WorkflowStateOptionsExtension setProceedOnExecuteFailure(final Class<? extends WorkflowState> proceedingState) {
1183
this.executeApiFailurePolicy(ExecuteApiFailurePolicy.PROCEED_TO_CONFIGURED_STATE);
1284
this.executeApiFailureProceedStateId(proceedingState.getSimpleName());
1385
return this;
1486
}
1587

16-
public WorkflowStateOptionsExtension setProceedOnExecuteFailure(final Class<? extends WorkflowState> proceedingState, WorkflowStateOptions stateOptions) {
88+
/**
89+
* Use setProceedAfterRetryExhaustedOnExecuteFailure instead
90+
* It's a renaming for better clarity.
91+
*/
92+
@Deprecated
93+
public WorkflowStateOptionsExtension setProceedOnExecuteFailure(final Class<? extends WorkflowState> proceedingState, WorkflowStateOptions stateOptionsOverride) {
1794
this.executeApiFailurePolicy(ExecuteApiFailurePolicy.PROCEED_TO_CONFIGURED_STATE);
1895
this.executeApiFailureProceedStateId(proceedingState.getSimpleName());
19-
this.executeApiFailureProceedStateOptions(stateOptions);
96+
this.executeApiFailureProceedStateOptions(stateOptionsOverride);
2097
return this;
2198
}
2299
}

src/main/java/io/iworkflow/core/mapper/StateMovementMapper.java

+36-2
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@
44
import io.iworkflow.core.Registry;
55
import io.iworkflow.core.StateDef;
66
import io.iworkflow.core.WorkflowDefinitionException;
7+
import io.iworkflow.core.WorkflowState;
78
import io.iworkflow.gen.models.ExecuteApiFailurePolicy;
9+
import io.iworkflow.gen.models.RetryPolicy;
810
import io.iworkflow.gen.models.StateMovement;
11+
import io.iworkflow.gen.models.WaitUntilApiFailurePolicy;
912
import io.iworkflow.gen.models.WorkflowStateOptions;
1013

1114
import static io.iworkflow.core.StateMovement.RESERVED_STATE_ID_PREFIX;
@@ -27,7 +30,7 @@ public static StateMovement toGenerated(final io.iworkflow.core.StateMovement st
2730
// Try to get the overrode stateOptions, if it's null, get the stateOptions from stateDef
2831
WorkflowStateOptions stateOptions = stateMovement.getStateOptionsOverride().orElse(null);
2932
if (stateOptions == null) {
30-
stateOptions = stateDef.getWorkflowState().getStateOptions();
33+
stateOptions = StateMovementMapper.validateAndGetStateOptions(stateDef);
3134
}
3235

3336
if (shouldSkipWaitUntil(stateDef.getWorkflowState())) {
@@ -59,7 +62,7 @@ public static void autoFillFailureProceedingStateOptions(WorkflowStateOptions st
5962
// fill the state options for the proceeding state
6063
String proceedStateId = stateOptions.getExecuteApiFailureProceedStateId();
6164
final StateDef proceedStatDef = registry.getWorkflowState(workflowType, proceedStateId);
62-
WorkflowStateOptions proceedStateOptions = proceedStatDef.getWorkflowState().getStateOptions();
65+
WorkflowStateOptions proceedStateOptions = StateMovementMapper.validateAndGetStateOptions(proceedStatDef);
6366
if (proceedStateOptions != null &&
6467
proceedStateOptions.getExecuteApiFailurePolicy() == ExecuteApiFailurePolicy.PROCEED_TO_CONFIGURED_STATE) {
6568
throw new WorkflowDefinitionException("nested failure handling is not supported. You cannot set a failure proceeding state on top of another failure proceeding state.");
@@ -76,4 +79,35 @@ public static void autoFillFailureProceedingStateOptions(WorkflowStateOptions st
7679
stateOptions.executeApiFailureProceedStateOptions(proceedStateOptions);
7780
}
7881
}
82+
83+
public static WorkflowStateOptions validateAndGetStateOptions(final StateDef stateDef){
84+
final WorkflowState state = stateDef.getWorkflowState();
85+
WorkflowStateOptions stateOptions = state.getStateOptions();
86+
if (stateOptions == null){
87+
return null;
88+
}
89+
if(stateOptions.getExecuteApiFailurePolicy() == ExecuteApiFailurePolicy.PROCEED_TO_CONFIGURED_STATE){
90+
// retry policy must be set
91+
if(stateOptions.getExecuteApiRetryPolicy() == null){
92+
throw new WorkflowDefinitionException("RetryPolicy must be set for the execute "+state.getStateId());
93+
}
94+
final RetryPolicy policy = stateOptions.getExecuteApiRetryPolicy();
95+
// either maximumAttempts or maximumAttemptsDurationSeconds must be set and greater than zero
96+
if(policy.getMaximumAttempts() == null && policy.getMaximumAttemptsDurationSeconds() == null){
97+
throw new WorkflowDefinitionException("Either maximumAttempts or maximumAttemptsDurationSeconds must be set for the execute "+state.getStateId());
98+
}
99+
}
100+
if(stateOptions.getWaitUntilApiFailurePolicy() == WaitUntilApiFailurePolicy.FAIL_WORKFLOW_ON_FAILURE){
101+
// retry policy must be set
102+
if(stateOptions.getWaitUntilApiRetryPolicy() == null){
103+
throw new WorkflowDefinitionException("RetryPolicy must be set for the waitUntil "+state.getStateId());
104+
}
105+
final RetryPolicy policy = stateOptions.getWaitUntilApiRetryPolicy();
106+
// either maximumAttempts or maximumAttemptsDurationSeconds must be set and greater than zero
107+
if(policy.getMaximumAttempts() == null && policy.getMaximumAttemptsDurationSeconds() == null){
108+
throw new WorkflowDefinitionException("Either maximumAttempts or maximumAttemptsDurationSeconds must be set for the waitUntil "+state.getStateId());
109+
}
110+
}
111+
return stateOptions;
112+
}
79113
}

src/test/java/io/iworkflow/integ/basic/ProceedOnStateStartFailWorkflowState1.java

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
import io.iworkflow.core.Context;
44
import io.iworkflow.core.StateDecision;
55
import io.iworkflow.core.WorkflowState;
6+
import io.iworkflow.core.WorkflowStateOptionsExtension;
67
import io.iworkflow.core.command.CommandRequest;
78
import io.iworkflow.core.command.CommandResults;
89
import io.iworkflow.core.communication.Communication;
910
import io.iworkflow.core.persistence.Persistence;
1011
import io.iworkflow.gen.models.RetryPolicy;
11-
import io.iworkflow.gen.models.WaitUntilApiFailurePolicy;
1212
import io.iworkflow.gen.models.WorkflowStateOptions;
1313

1414
public class ProceedOnStateStartFailWorkflowState1 implements WorkflowState<String> {
@@ -40,8 +40,8 @@ public StateDecision execute(Context context, String input, CommandResults comma
4040

4141
@Override
4242
public WorkflowStateOptions getStateOptions() {
43-
return new WorkflowStateOptions()
44-
.waitUntilApiRetryPolicy(new RetryPolicy().maximumAttempts(2))
45-
.waitUntilApiFailurePolicy(WaitUntilApiFailurePolicy.PROCEED_ON_FAILURE);
43+
return new WorkflowStateOptionsExtension()
44+
.setProceedWhenWaitUntilRetryExhausted(true)
45+
.waitUntilApiRetryPolicy(new RetryPolicy().maximumAttempts(2));
4646
}
4747
}

src/test/java/io/iworkflow/integ/stateapifail/StateFailProceedToRecoverBasic.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public class StateFailProceedToRecoverBasic extends StateFailBasic {
88
@Override
99
public WorkflowStateOptions getStateOptions() {
1010
return new WorkflowStateOptionsExtension()
11-
.setProceedOnExecuteFailure(StateRecoverBasic.class)
11+
.setProceedWhenExecuteRetryExhausted(StateRecoverBasic.class)
1212
.executeApiRetryPolicy(
1313
new RetryPolicy()
1414
.maximumAttempts(1)

0 commit comments

Comments
 (0)