Skip to content

Workflow actions

Gurmit Teotia edited this page Apr 25, 2020 · 32 revisions

Workflow actions are the instructions generated in response to a Amazon SWF event. These workflow actions are represented by Guflow.Decider.WorkflowAction class in Guflow.

Good to know: Workflow actions are additive, you can send multiple workflow actions in response to an SWF event.

Supported workflow actions:

  1. CompleteWorkflow: Completes the workflow immediately with provided result. By default this action is generated when workflow execution is reach at the end of the workflow. In following example CompleteWorkflow action will be generated after "ShipOrder" Lambda:

    [WorkflowDescription("1.0")]
    public class OrderWorkflow : Workflow
    {
      public OrderWorkflow()
      {
        ScheduleLambda("ReserveItems");
        ScheduleLambda("ChargeCustomer").AfterLambda("ReserveItems");
        ScheduleLambda("ShipOrder").AfterLambda("ChargeCustomer");
      }
    }

    You can also complete workflow with custom result as shown below:

    [WorkflowDescription("1.0")]
    public class OrderWorkflow : Workflow
    {
      public OrderWorkflow()
      {
        ScheduleLambda("ReserveItems");
        ScheduleLambda("ChargeCustomer").AfterLambda("ReserveItems");
        ScheduleLambda("ShipOrder").AfterLambda("ChargeCustomer");
        ScheduleAction(_=>CompleteWorkflow("Custom result")).AfterLambda("ShipOrder");
      }
    }

    You can return CompleteWorkflow action in response to any SWF event. In following example workflow is completed on signal

    [WorkflowDescription("1.0")]
    public class OrderWorkflow : Workflow
    {
       ...
      [WorkflowEvent(EventName.Signal)]
      public WorkflowAction OnSignal()
      {
        return CompleteWorkflow("some result");
      }
      
    }
  2. FailWorkflow: Fails the workflow immediately with reason and details. By default FailWorkflow action is returned in response to any failed or timedout event. It is also returned in response to child workflow terminated event. You can return FailWorkflow action in response to any SWF event. In following example workflow is failed when activity is completed with a specific result:

    [WorkflowDescription("1.0")]
    public class OrderWorkflow : Workflow
    {
       public OrderWorkflow()
       {
         ScheduleActivity<ReserveOrder>()
          .OnCompletion(e=>e.Result().Status=="Pending"? FailWorkflow("Pending", "Order")
                                                       : e.DefaultAction());
         ...
       }
      
    }
  3. CancelWorkflow: Cancel the workflow immediately with details. By default CancelWorkflow action is returned when a cancel request is made to a workflow or an activity or a child workflow is cancelled. You can return CancelWorkflow action in response to any event. In following example workflow is cancelled on receiving a specific signal:

     [WorkflowDescription("1.0")]
     public class OrderWorkflow : Workflow
     {
       
      [WorkflowEvent(EventName.Signal)]
      public WorkflowAction OnSignal(string signalName)
      {
         return signalName =="cancel it"? CancelWorkflow("Cancelled by signal"): Ignore;
      }
     }
  4. StartWorkflow: It will cause the workflow to start execution from beginning. Unlike RestartWorkflow it will not close the current running workflow in Amazon SWF. By default StartWorkflow action is sent in response to workflow started event, which is raised when workflow is started. You can send StartWorkflow action in response to any SWF event. Following example uses StartWorkflow action to execute the workflows steps in a loop:

     WorkflowDescription("1.0")]
     public class ProcessLogWorkflow : Workflow
     {
         public ProcessLogWorkflow()
         {
            ScheduleLambda("ProcessLog");
    
             ScheduleLambda("UpdateMatrices").AfterLambda("ProcessLog")
                 
             ScheduleAction(_ => LastEventId>20000? CompleteWorkflow("done") : StartWorkflow())
     		.AfterLambda("UpdateMatrices");
         }
     }
  5. RestartWorkflow: It will close the current running workflow and restart a new instance of workflow in Amazon SWF. This is quite useful in containing the history event. Amazon SWF allows only 25000 history events for a workflow, which is a pretty decent number for a workflow however you may run over this limit in a recurrent workflow. Following example restart the workflow when activity is scheduled 100 times:

    [WorkflowDescription("1.0")]
    public class ProcessLogWorkflow : Workflow
    {
        public ProcessLogWorkflow()
        {
            ScheduleActivity<ProcessLog>()
    		 .OnCompletion(e => Reschedule(e).After(TimeSpan.FromMinutes(1)).UpTo(times:100));
            ScheduleAction(a => RestartWorkflow()).AfterActivity<ProcessLog>();
        }
    }

    You can return RestartWorkflow action in response to any SWF event.

  6. Continue: It will continue the execution of workflow. It will schedule the children, if there are any, otherwise it will complete the workflow. By default "Continue" workflow action is send when a child workflow item -activity, timer, Lambda function or child workflow is successfully completed. You can only send "Continue" workflow action in response to events of child item and not in response to workflow specific events like signal, cancel request etc. In the following example workflow continue the execution when Lambda function -"SendEmail" fails:

    [WorkflowDescription("1.0")]
    public class OrderWorkflow : Workflow
    {
       public OrderWorkflow()
       {
         ...
         ScheduleLambda("ChargeCustomer");
         ScheduleLambda("SendEmail").AfterLambda("ChargeCustomer")
       	.OnFailure(Continue);
         ScheduleLambda("ShipOrder");
         
       }
    }
  7. Ignore: It means no action. It can be used to pause/stop the workflow execution or to ignore the event. By default Ignore action is send in response to workflow signal event. You can use Ignore action in response to any workflow event. In following example it is being used to pause the execution of workflow:

      [WorkflowDescription("1.0")]
      public class OrderWorkflow : Workflow
      {
         public OrderWorkflow()
         {
           ScheduleLambda("Reserve")
            .OnFailure(e=>e.Reason ="OutOfStock"? Ignore: e.DefaultAction());
         }
      }

    If Ignore workflow action is used in a branch then to maintain deterministic behaviour workflow will try to schedule the first joint item as explained in Deflow algorithm

  8. Reschedule: Reschedule a workflow item. This action supported only in the context of Lambda function, activity, timer and child workflow events. It further expose the method to let you control number of times a item should be rescheduled or the interval between each reschedule. e.g.:

     //Reschedule the lambda immediately on failure
     ScheduleLambda("ProcessFile").OnFailure(Reschedule);
    
    //Reschedule the Lambda function after 10 seconds on failure
     ScheduleLambda("ProcessFile").OnFailure(e=>Reschedule(e).After(TimeSpan.FromSeconds(10));
    
    //Reschedule the Lambda function after 10 seconds with retry limit of 2. When the limit it is reached it takes 
    //default action on that event. In case of failed event it will fail the workflow     
     ScheduleLambda("ProcessFile").OnFailure(e=>Reschedule(e).After(TimeSpan.FromSeconds(10).UpTo(times:2));
  9. Jump: Jump to a particular item in workflow and start execution from there. While jumping to a target element, it ignore the status of parent items (activities, timer or Lambda functions) and its "When" clause and simply schedule it. It let you jump to target element immediately or after a configured timeout.

    Note:

    1. Jump will fail if target item (activity, timer...) is already active. It is recommended to have a guard condition before jump instruction.
    2. From an workflow item(activity, timer) you can only jump it child or parent branches...jumping to sibling branches are not allowed. In following workflow setup, from activity G you can not jump to A or D activity but jumping to rest of activities are allowed:
                            A     B     C
                            |     |     |
                            D     E     F 
                            |     |     |
                            |     `` |```
                            |        G
                            |        |
                            ````|`````
                                H
                                |
                                J
    

    In following example workflow is resumed on signal when it receive the inventory:

     [WorkflowDescription("1.0")]
      public class OrderWorkflow : Workflow
      {
         public OrderWorkflow()
     	{
     	  
     	  ScheduleActivity<ReserveActivity>();
     	    .OnFailure(e=>e.Reason ="OutOfStock"? Ignore: e.DefaultAction());
     	  ScheduleActivity<ChargeCustome>().AfterActivity<ReserveActivity>();
        }
        
        [WorkflowEvent(EventName.Signal)]
        public WorkflowAction OnSignal(string signalName)
        {
            if (signalName == "InventoryUpdated" && Activity<ReserveActivity>().LastFailedEvent()?.Reason=="OutOfStock")
                 return Jump.ToActivity<ReserveOrder>().After(TimeSpan.FromSeconds(20));
    
             return Ignore;
        }

    There are many other examples related to Jump workflow action. Examples also show how you can simulate the loop using Jump workflow action. You can use Jump workflow action in response to any Amazon SWF event.

  10. Trigger: This action is useful in supporting deterministic behaviour for branch execution. It is further explained in Deflow algorithm. Trigger workflow action can be used in response to the events of only schedulable items - timer, activity, Lambda functions. By default Trigger workflow action is generated when:

    • The "When" API returns false for a workflow item- Lambda, timer, child workflow or activity. In following example workflow will try to trigger the scheduling of "ChargeCustomer" Lambda function when customer is not choosing the seat:

      [WorkflowDescription("1.0")]
      public class BookHolidaysWorkflow : Workflow
      {
        public BookHolidaysWorkflow()
        {
            ScheduleLambda("BookFlight");
            ScheduleLambda("ChooseSeat").AfterLambda("BookFlight").When(_=>Input.ChooseSeat);
            ScheduleLambda("ChooseFood").After("ChooseSeat");
      
            ScheduleLambda("BookHotel")
            ScheduleLambda("BookDinner").AfterLambda("BookHotel");
      
            ScheduleLambda("ChargeCustomer").AfterLambda("ChooseSeat").AfterLambda("BookDinner");
            
            ScheduleLambda("SendEmail").AfterLambda("ChargeCustomer");
        }
      }     
    • Ignore workflow action is returned in response to an event of a workflow item. In following example if you ignore the failure of "ChooseSeat" Lambda function then it will trigger the "ChargeCustomer" (I'm not sure how sensible this behaviour is for this business problem though):

      [WorkflowDescription("1.0")]
      public class BookHolidaysWorkflow : Workflow
      {
         public BookHolidaysWorkflow()
         {
             ScheduleLambda("BookFlight");
             ScheduleLambda("ChooseSeat").AfterLambda("BookFlight")
                  .OnFailure(_=Ignore);
             ScheduleLambda("ChooseFood").After("ChooseSeat");
      
             ScheduleLambda("BookHotel")
             ScheduleLambda("BookDinner").AfterLambda("BookHotel");
      
             ScheduleLambda("ChargeCustomer").AfterLambda("ChooseSeat").AfterLambda("BookDinner");
             
             ScheduleLambda("SendEmail").AfterLambda("ChargeCustomer");
         }
       }     
  11. DefaultAction: Return default action for a given a event. e.g. for activity failed event, default action is to fail the workflow so it will return FailWorkflow action. In following example Lambda function will be reschedule when it failed with specific error otherwise default action will be returned:

    [WorkflowDescription("1.0")]
    public class TranscodeVideo : Workflow
    {
       public TranscodeVideo()
       {
         ...
         ScheduleLambda("DownloadVideo")
           .OnFailure(e=>e.Reason ="Timedout"? Reschedule(e): e.DefaultAction());
         ...
      }
    
    }
  12. CancelRequest: It helps you to generate cancel requests for external workflows, child workflows, activities and timer. Lambda function does not support cancellation. Examples:

    // Cancel all the active item- activities and child workflows- Lambda will be automatically filter out
     CancelRequest.For(WorkflowItems.Where(i => i.IsActive));
    
    //Cancel the specific activity. Please make sure it is active before you send cancellation request
     if(Activity<ReserveOrder>().IsActive)
       return CancelRequest.ForActivity<ReserveOrder>();
    
    //Cancel the specific child workflow. Please make sure it is active before you send cancellation request
     if(ChildWorkflow<ChargeCustomer>().IsActive)
       return CancelRequest.ChildWorkflow<ChargeCustomer>();
    
    // Cancel request of external workflow. 
     CancelRequest.ForWorkflow("workflowid", "runid");

    By default when a workflow item - child workflow or activity is cancelled, "CancelWorkflow" action is returned however you can easily write your custom logic where you want to cancel the workflow when all the workflow items are cancelled.

  13. RecordMarker: Using marker you can store any custom information in the workflow history. You can query all markers in workflow history events by query APIs. Some of the scenarios where markers can be used are:

    • Use marker as checkpoint in history events and use it in custom retrieval of the history events.

    • Store workflow specific information in the markers. You can query for all recorded markers using query API as shown in the following example:

      var events = Workflow.AllMarkerEvents;

    Note: Guflow internally use markers in the implementation of the Wait for signals APIs.

  14. Signal: It can be used to send signals to other running workflows or child workflows. You can send signal in response to any SWF event. e.g.

     //Send the signal "signal1" to external workflow with WorkflowID - "1" and RunID as "2"
     Signal("signal1", new {CustomerId=10}).ToWorkflow(workflowId: "1", runId: "2");
    
    //Send the signal to child workflow "ChargeCustomer". It will automatically figure out its workflow Id and run Id.
    Signal("signal1", new {CustomerId=10}).ToChildWorkflow<ChargeCustomer>();
    
    //Reply to workflow, which has sent the signal to us.
    [WorkflowEvent(EventName.Signal)]
    public WorkflowAction OnSignal(WorkflowSignaledEvent @event)
            => Signal("signal", "detail").ReplyTo(@event);

Default workflow actions: Here is the summary of default workflow actions send in response to the SWF events:

  • StartWorkflow action on workflow started event
  • Continue action when a workflow item is successfully completed
  • FailWorkflow action on any failure/timeout
  • FailWorkflow on child workflow terminated event
  • Ignore action on signal event when there is no waiting workflow item.
  • CancelWorkflow action on receiving the cancel request
  • CancelWorkflow when a workflow item is successfully cancelled
  • CompleteWorkflow when no more children to schedule and workflow is not active

Notes:

  1. Workflow actions are additive. You can return more than one compatible workflow actions in response to an event as shown in following example:
    [WorkflowDescription("1.0")]
    public class TranscodeWorkflow : Workflow
    {
      public TranscodeWorkflow()
      { 
        ScheduleActivity<DownloadActivity>().OnCompletion(a=>Continue(a)+RecordMarker("name", "details"));
        ...
      }
    }
  2. Return compatible workflow actions in response to an event. e.g. returning two workflow actions- one to complete the workflow and other to schedule an activity together does not make sense. Guflow has a basic filtering mechanism whereby it gives priority to workflow closing WorkflowActions and filter out the non-closing WorkflowAction. e.g. Guflow will filter out the workflow action to schedule the activity when it is generated along with CompleteWorkflow action. If there are multiple workflow closing WorkflowActions generated from different parallel branches then it gives priority in following order:
    • FailWorkflow
    • CancelWorkflow
    • RestartWorkflow
    • CompleteWorkflow

If incompatible workflow actions are passed to Amazon SWF then a fault/error event will generated which, if needed, can be handled using workflow events. Please see error handling for more details.

Clone this wiki locally