skip to Main Content

In AWS Step Functions I have a simple state machine with three states. StateA and StateB are inside a ‘Parallel’ state. StateC is after the ‘Parallel’ state.

By default, StateC is executed when both StateA and StateB are completed. However, my requirement is to run StateC when either one of the parallel branches are complete, and without waiting for the other branch.

The following is the ASL code for the state machine. Account number is masked with *:

{
  "Comment": "Parellel state test",
  "StartAt": "Parallel",
  "States": {
    "Parallel": {
      "Type": "Parallel",
      "Branches": [
        {
          "StartAt": "StateA",
          "States": {
            "StateA": {
              "Type": "Pass",
              "End": true
            }
          }
        },
        {
          "StartAt": "StateB",
          "States": {
            "StateB": {
              "Type": "Task",
              "Resource": "arn:aws:states:::lambda:invoke.waitForTaskToken",
              "Parameters": {
                "Payload": { 
                  "token.$": "$$.Task.Token"
                },
                "FunctionName": "arn:aws:lambda:eu-west-1:************:function:rere-sleep-five-seconds:$LATEST"
              },              
              "End": true
            }
          }
        }
      ],
      "Next": "StateC"
    },
    "StateC": {
      "Type": "Pass",
      "End": true
    }
  }
}

As you can see from the following image. StateC is never executed due to the failure of StateB. How can I configure StateC to run when either of the parallel branches are done?

State Machine execution

Some of the questions describe a similar scenario, however, none is answering this specific requirement.

2

Answers


  1. You can’t really do this "natively" since you have to wait until all branches in the parallel step finish. This is similar to how if one branch fails, the entire step will exit early.

    The "easiest" way is to shuffle things around so that State C be triggered in some idempotent way and call that task at the end of branch in your parallel step.

    Login or Signup to reply.
  2. There is a hacky workaround to achieve this by ‘inverting’ the behavior of success and failure. In other words, if one of the branches fails, catch the failure and move to a pass state. If one of the branches succeeds, move to fail state so you don’t have to wait for the other branch.

    Next, the parallel state should catch all errors and go to a choice state. The choice state should check the output of the parallel state. If at least one of the branches has no error field in its output, then success.

    The wait state here is for testing purposes.

    {
      "Comment": "Parellel state test",
      "StartAt": "Parallel",
      "States": {
        "Parallel": {
          "Type": "Parallel",
          "Branches": [
            {
              "StartAt": "StateA",
              "States": {
                "StateA": {
                  "Type": "Task",
                  "Resource": "arn:aws:states:::lambda:invoke",
                  "OutputPath": "$.Payload",
                  "Parameters": {
                    "Payload.$": "$",
                    "FunctionName": "arn:aws:lambda:us-east-1:***********:function:RandomlySucceedOrFail:$LATEST"
                  },
                  "Next": "Success A",
                  "Catch": [
                    {
                      "ErrorEquals": [
                        "States.ALL"
                      ],
                      "Next": "Fail A"
                    }
                  ]
                },
                "Fail A": {
                  "Type": "Pass",
                  "End": true
                },
                "Success A": {
                  "Type": "Fail"
                }
              }
            },
            {
              "StartAt": "Wait",
              "States": {
                "Wait": {
                  "Type": "Wait",
                  "Seconds": 10,
                  "Next": "StateB"
                },
                "StateB": {
                  "Type": "Task",
                  "Resource": "arn:aws:states:::lambda:invoke",
                  "OutputPath": "$.Payload",
                  "Parameters": {
                    "Payload.$": "$",
                    "FunctionName": "arn:aws:lambda:us-east-1:***********:function:RandomlySucceedOrFail:$LATEST"
                  },
                  "Next": "SuccessB",
                  "Catch": [
                    {
                      "ErrorEquals": [
                        "States.ALL"
                      ],
                      "Next": "FailB"
                    }
                  ]
                },
                "FailB": {
                  "Type": "Pass",
                  "End": true
                },
                "SuccessB": {
                  "Type": "Fail"
                }
              }
            }
          ],
          "Next": "Choice",
          "Catch": [
            {
              "ErrorEquals": [
                "States.ALL"
              ],
              "Next": "Choice"
            }
          ]
        },
        "Choice": {
          "Type": "Choice",
          "Choices": [
            {
              "Or": [
                {
                  "Not": {
                    "Variable": "$[0].Error",
                    "IsPresent": true
                  }
                },
                {
                  "Not": {
                    "Variable": "$[1].Error",
                    "IsPresent": true
                  }
                }
              ],
              "Next": "StateC"
            }
          ],
          "Default": "Fail3"
        },
        "Fail3": {
          "Type": "Fail"
        },
        "StateC": {
          "Type": "Pass",
          "End": true
        }
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search