alwaysAStudent
alwaysAStudent

Reputation: 2264

AWS SWF Restart workflow under specific conditions

I have a SWF workflow and activity. Below is structure:

WorkflowClientImpl Class:

 class TempWorkflowImpl() {
     @Override 
     public void execute() {
           new TryCatchFinallly {
                 @Override
                 protected void doTry() throws Throwable {
                        activityClient.invoke();
                 }
                 @Override
                 protected void doFinally() throws Throwable {
                        // Clean up code
                 }
                 @Override
                 protected void doCatch() throws Throwable {
                        // Handle Exception
                 }                    
           }
     } 
 }

ActivityClientImpl Class:

 class TempActivityImpl()  {
     @Override 
     public void invoke() {
         // Perform some logic
         // Check if API call (API_Call_A) is made previously
         // If not Invoke API_Call_A.
         // If yes, throw exception  

     }
 }

The activity class makes an API call which an asynchronous method. The action defined in the API call takes approx an hour to complete. Sometimes the action can fail while it is executing due to certain reasons. This API call is defined on service that I do not have access to. Is there a way I can sleep the activity so that it can check after an hour whether the action was successful or not. If not successful, I would be re-invoking the API call. Let us assume that this time the action will succeed and we do not end up in an infinite loop of the API call attempts.

Thread.sleep() appears to be one way, though I am not sure that is most appropriate way. I also found we can restart the entire workflow using

 Promise<Void> timer = decisionContextProvider.getDecisionContext().getWorkflowClock().createTimer(TimeUnit.MINUTES.toSeconds(TimeinMinutes));
 continueAsNew(timer);

To use the above, I can return from the activity method a value for TimeinMinutes after API call and then restart the workflow after an hour.

Is the above approach most appropriate? Or is there a better way to do this?

Thanks

Upvotes: 0

Views: 537

Answers (1)

Maxim Fateev
Maxim Fateev

Reputation: 6870

There is no need to call continueAsNew unless your workflow history is large (like after calling activity 100 times). Just use @Asynchronous method or Task to wait on promises. I would model your workflow as two activties: invoke and checkResult and execute checkResult after delay and use @ExponentialResult to retry it until result is available.

class TempWorkflowImpl() {
   private final WorkflowClock clock = decisionContextProvider.getDecisionContext().getWorkflowClock()
   @Override 
   public void execute() {
       new TryCatchFinallly {
             @Override
             protected void doTry() throws Throwable {
                   invoke();
             }
             @Override
             protected void doFinally() throws Throwable {
                    // Clean up code
             }
             @Override
             protected void doCatch() throws Throwable {
                    // Handle Exception
             }                    
       }
   } 

   @Asynchronous
   // On ServiceFailureException retry from the beginning
   @ExponentialRetry(initialRetryIntervalSeconds=300, exceptionsToRetry=ServiceFailureException.class) 
   private Promise<ResultType>  invoke() {
                    Promise<Void> invoked = activityClient.invoke();
                    Promise<ResultType> result = checkResultAfterDelay(invoked);
                    processResult(result);
   }

   @Asynchronous
   private Promise<ResultType>  checkResultAfterDelay(Promise<Void> invoked) {
      Promise<Void> timer = clock.createTimer(TimeUnit.MINUTES.toSeconds(60));
      return checkResult(timer);
   }

   @Asynchronous
   // Automatically retry on ResultUnavailableException
   @ExponentialRetry(initialRetryIntervalSeconds=300, exceptionsToRetry=ResultUnavailableException.class) 
   private Promise<ResultType> checkResult(Promise<Void> timer) {
      return activityClient.checkResult();
   }

   @Asynchronous
   private processResult(Promise<ResultType>  result) {
     ....
   }

}

Upvotes: 1

Related Questions