Managing error flow in AWS step-functions

六月ゝ 毕业季﹏ 提交于 2019-12-10 10:35:46

问题


I have an AWS step-function/state-machine of Lambda functions written primarily in Javascript (although one is in Java) and I'd like to manage the error processing better.

I have no problem with having an error condition being caught and then forwarded to another state in the flow. So for instance, the following state definition in my state machine passes execution to the NotifyOfError state where I am able to email and sms appropriately about the error state.

Closure:
  Type: Task
  Resource: >-
    arn:aws:lambda:#{AWS::Region}:#{AWS::AccountId}:function:xxx-services-${opt:stage}-transportClosure
  Next: WaitForCloudWatch
  Catch:
    - ErrorEquals:
        - "States.ALL"
      ResultPath: "$.error-info"
      Next: NotifyOfError

However, rather than hand ALL errors to this one state there are a few errors I'd like handle differently. So at first I thought that if I threw a Javascript/Node error with a given "name" then that name would be something I could branch off of in the ErrorEquals configuration. Example:

 catch(e) {
      if (e.message.indexOf('something') !== -1) {
           e.name = "SomethingError";
               throw e;
      }

but soon realized that name was only being prepended to the Cause portion of the step-function and not something that would branch. I then tried extending the base Error class like so:

export default class UndefinedAssignment extends Error {
  constructor(e: Error) {
    super(e.message);
    this.stack = e.stack;
  }
}

but throwing this error actually did nothing, meaning that by the time it showed up in the Step Function the Error's type was still just "Error":

"error-info": {
    "Error": "Error",
    "Cause": "{\"errorMessage\":\"Error: the message",\"errorType\":\"Error\",\"stackTrace\":[\"db.set.catch.e (/var/task/lib/prepWorker/Handler.js:247:23)\",\"process._tickDomainCallback (internal/process/next_tick.js:135:7)\"]}"
}

So I'm still unclear how I can distinguish errors sourced in Node that are branchable within the step function.

Note: with Java, it appears it does pickup the error class correctly (although I've done far less testing on the Java side)


回答1:


Here's how I get Step Functions to report a custom error and message as its Error and Cause. Note I'm using the Node.js 8.10 Lambda runtime with async and try/catch.

exports.handler = async (event) => {
  function GenericError(name, message) {
    this.name = name;
    this.message = message;
  }
  GenericError.prototype = new Error();
  try {
    // my implementation which might throw an error
    // ...
  }
  catch (e) {
    console.log(e);
    let error = new GenericError('CustomError', 'my message');
    throw error;
  }
};

Note for simplicity I'm ignoring the error object from catch(e) here. You could also feed its stack into the GenericError if wanted.

This lambda function returns:

{
  "errorMessage": "my message",
  "errorType": "CustomError",
  "stackTrace": [
    "exports.handler (/var/task/index.js:33:28)"
  ]
}

Step Functions turns this into:

{
  "error": "CustomError",
  "cause": {
    "errorMessage": "my message",
    "errorType": "CustomError",
    "stackTrace": [
      "exports.handler (/var/task/index.js:33:28)"
    ]
  }
}

in its LambdaFunctionFailed event history, and ultimately converts it again into this state output (depending on our ResultPath - here without any):

{
  "Error": "CustomError",
  "Cause": "{\"errorMessage\":\"my message\",\"errorType\":\"CustomError\",\"stackTrace\":[\"exports.handler (/var/task/index.js:33:28)\"]}"
}



回答2:


You should return thrown exception from Lambda using callback. Example Cloud Formation template creating both lambda and state machine:

AWSTemplateFormatVersion: 2010-09-09
Description: Stack creating AWS Step Functions state machine and lambda function throwing custom error. 

Resources:
  LambdaFunction:
    Type: AWS::Lambda::Function
    Properties:
      Handler: "index.handler"
      Role: !GetAtt LambdaExecutionRole.Arn
      Code:
        ZipFile: |
          exports.handler = function(event, context, callback) {
              function SomethingError(message) {
                  this.name = "SomethingError";
                  this.message = message;
              }
              SomethingError.prototype = new Error();

              const error = new SomethingError("something-error");
              callback(error);
          };
      Runtime: "nodejs6.10"
      Timeout: 25

  StateMachine:
    Type: AWS::StepFunctions::StateMachine
    Properties:
      RoleArn: !GetAtt StatesExecutionRole.Arn
      DefinitionString: !Sub
        - >
          {
            "Comment": "State machine for nodejs error handling experiment",
            "StartAt": "FirstState",
            "States": {
              "FirstState": {
                "Type": "Task",
                "Resource": "${ThrowErrorResource}",
                "Next": "Success",
                "Catch": [
                  {
                    "ErrorEquals": ["SomethingError"],
                    "ResultPath": "$.error",
                    "Next": "CatchSomethingError"
                  }
                ]
              },
              "Success": {
                "Type": "Pass",
                "End": true
              },
              "CatchSomethingError": {
                "Type": "Pass",
                "Result": {
                  "errorHandlerOutput": "Huh, I catched an error"
                },
                "ResultPath": "$.errorHandler",
                "End": true
              }
            }
          }
        - ThrowErrorResource: !GetAtt LambdaFunction.Arn

  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
        - Effect: Allow
          Principal:
            Service:
              - lambda.amazonaws.com
          Action:
            - sts:AssumeRole

  StatesExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - !Sub states.${AWS::Region}.amazonaws.com
            Action: sts:AssumeRole
      Policies:
        - PolicyName: ExecuteLambda
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - lambda:InvokeFunction
                Resource: arn:aws:lambda:*:*:function:*

Essential part is Lambda Function definition:

exports.handler = function(event, context, callback) {
    function SomethingError(message) {
        this.name = "SomethingError";
        this.message = message;
    }
    SomethingError.prototype = new Error();

    const error = new SomethingError("something-error");
    callback(error);
};

Custom error with custom name is defined here. Of course you can also simply overwrite name (but I do not recommend that):

exports.handler = function(event, context, callback) {
    var e = new Error();
    e.name = "SomethingError";
    callback(e);
};

Error returned like that will be passed to Step Functions without losing error name. I suggest creating some top try-catch statement in Lambda Function where you would simply call callback with error.



来源:https://stackoverflow.com/questions/49373039/managing-error-flow-in-aws-step-functions

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!